mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-28 12:36:35 +00:00
Sketch out animating .ani cursors
This commit is contained in:
parent
2d6257712e
commit
3a9d32dfee
3 changed files with 50 additions and 16 deletions
|
|
@ -5,6 +5,7 @@ import { useTypedSelector } from "../hooks";
|
|||
import * as Selectors from "../selectors";
|
||||
import { SkinImages } from "../types";
|
||||
import { createSelector } from "reselect";
|
||||
import * as Utils from "../utils";
|
||||
import Css from "./Css";
|
||||
import ClipPaths from "./ClipPaths";
|
||||
|
||||
|
|
@ -67,19 +68,41 @@ const getCssRules = createSelector(
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
Object.keys(cursorSelectors).forEach((cursorName) => {
|
||||
const cursorUrl = skinCursors[cursorName];
|
||||
if (cursorUrl) {
|
||||
const cursor = skinCursors[cursorName];
|
||||
if (cursor) {
|
||||
cursorSelectors[cursorName].forEach((selector) => {
|
||||
cssRules.push(
|
||||
`${
|
||||
// TODO: Fix this hack
|
||||
// Maybe our CSS name spacing should be based on some other class/id
|
||||
// than the one we use for defining the main div.
|
||||
// That way it could be shared by both the player and the context menu.
|
||||
selector.startsWith("#webamp-context-menu") ? "" : CSS_PREFIX
|
||||
} ${selector} {cursor: url(${cursorUrl}), auto}`
|
||||
);
|
||||
const normalizedSelector = `${
|
||||
// TODO: Fix this hack
|
||||
// Maybe our CSS name spacing should be based on some other class/id
|
||||
// than the one we use for defining the main div.
|
||||
// That way it could be shared by both the player and the context menu.
|
||||
selector.startsWith("#webamp-context-menu") ? "" : CSS_PREFIX
|
||||
} ${selector}`;
|
||||
|
||||
switch (cursor.type) {
|
||||
case "cur":
|
||||
cssRules.push(
|
||||
`${normalizedSelector} {cursor: url(${cursor.url}), auto}`
|
||||
);
|
||||
break;
|
||||
case "ani": {
|
||||
const animationName = `ani-cursor-${Utils.uniqueId()}`;
|
||||
const frames = cursor.urls.map((url, i) => {
|
||||
const percent = (i / (cursor.urls.length - 1)) * 100;
|
||||
return `${percent}% { cursor: url(${url}), auto; }`;
|
||||
});
|
||||
const durationMs = cursor.iDispRate * 16 * frames.length;
|
||||
const framesCss = frames.join("\n");
|
||||
const keyframes = `@keyframes ${animationName} { ${framesCss} }`;
|
||||
cssRules.push(keyframes);
|
||||
cssRules.push(
|
||||
`${normalizedSelector} { animation: ${animationName} ${durationMs}ms infinite; }`
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import JSZip from "jszip";
|
||||
import { PlaylistStyle, SkinGenExColors } from "./types";
|
||||
import { PlaylistStyle, SkinGenExColors, CursorImage } from "./types";
|
||||
import SKIN_SPRITES, { Sprite } from "./skinSprites";
|
||||
import { DEFAULT_SKIN } from "./constants";
|
||||
import * as Utils from "./utils";
|
||||
|
|
@ -129,7 +129,7 @@ const ANI_MAGIC_BASE_64 = "UklGR";
|
|||
export async function getCursorFromFilename(
|
||||
zip: JSZip,
|
||||
fileName: string
|
||||
): Promise<string | null> {
|
||||
): Promise<CursorImage | null> {
|
||||
const file = await getFileFromZip(zip, fileName, "CUR", "base64");
|
||||
if (file == null) {
|
||||
return null;
|
||||
|
|
@ -141,10 +141,10 @@ export async function getCursorFromFilename(
|
|||
throw new Error("We just read this file, how can it be null??");
|
||||
}
|
||||
const ani = parseAni(fileArr.contents as Uint8Array);
|
||||
return ani.urls[0];
|
||||
return { type: "ani", urls: ani.urls, iDispRate: ani.iDispRate };
|
||||
}
|
||||
|
||||
return `data:image/x-win-bitmap;base64,${contents}`;
|
||||
return { type: "cur", url: `data:image/x-win-bitmap;base64,${contents}` };
|
||||
}
|
||||
|
||||
export async function getPlaylistStyle(zip: JSZip): Promise<PlaylistStyle> {
|
||||
|
|
|
|||
|
|
@ -75,9 +75,20 @@ export type Band =
|
|||
|
||||
export type Slider = Band | "preamp";
|
||||
|
||||
export type CursorImage =
|
||||
| {
|
||||
type: "cur";
|
||||
url: string;
|
||||
}
|
||||
| {
|
||||
type: "ani";
|
||||
iDispRate: number;
|
||||
urls: string[];
|
||||
};
|
||||
|
||||
// TODO: Use a type to ensure these keys mirror the CURSORS constant in
|
||||
// skinParser.js
|
||||
export type Cursors = { [cursor: string]: string };
|
||||
export type Cursors = { [cursor: string]: CursorImage };
|
||||
|
||||
export type GenLetterWidths = { [letter: string]: number };
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue