Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/examples/src/examples/ui/ExampleUI.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ const createGame = () => {
{
name: "kenpixel",
type: "fontface",
src: `url(${base}font/kenvector_future.woff2)`,
src: `${base}font/kenvector_future.woff2`,
},
];

Expand Down
1 change: 1 addition & 0 deletions packages/melonjs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [18.1.0] (melonJS 2)

### Added
- Loader: fontface assets now support `baseURL` — font paths are resolved consistently with other asset types
- TextureAtlas: new `getAnimationSettings()` method for extending Sprite with texture atlas animations
- TMX: automatically decompose concave collision polygons into convex triangles using earcut triangulation, instead of throwing an error
- Ellipse: add rotation and matrix transform support (#583, #771)
Expand Down
26 changes: 19 additions & 7 deletions packages/melonjs/src/loader/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export function setOptions(options) {
* @name setBaseURL
* @memberof loader
* @public
* @param {string} type - "*", "audio", "video", "binary", "image", "json", "js", "tmx", "tsx"
* @param {string} type - "*", "audio", "video", "binary", "image", "json", "js", "tmx", "tsx", "fontface"
* @param {string} [url="./"] - default base URL
* @example
* // change the base URL relative address for audio assets
Expand All @@ -140,8 +140,7 @@ export function setBaseURL(type, url = "./") {
baseURL["js"] = url;
baseURL["tmx"] = url;
baseURL["tsx"] = url;
// XXX ?
//baseURL["fontface"] = url;
baseURL["fontface"] = url;
}
Comment on lines 140 to 144
}

Expand Down Expand Up @@ -312,7 +311,7 @@ function onLoadingError(res) {
* // JavaScript file
* {name: "plugin", type: "js", src: "data/js/plugin.js"}
* // Font Face
* { name: "'kenpixel'", type: "fontface", src: "url('data/font/kenvector_future.woff2')" }
* { name: "'kenpixel'", type: "fontface", src: "data/font/kenvector_future.woff2" }
* // video resources
* {name: "intro", type: "video", src: "data/video/"}
*/
Expand Down Expand Up @@ -385,7 +384,7 @@ export function setParser(type, parserFn) {
* // JavaScript file
* {name: "plugin", type: "js", src: "data/js/plugin.js"},
* // Font Face
* {name: "'kenpixel'", type: "fontface", src: "url('data/font/kenvector_future.woff2')"},
* {name: "'kenpixel'", type: "fontface", src: "data/font/kenvector_future.woff2"},
* // video resources
* {name: "intro", type: "video", src: "data/video/"},
* // base64 encoded video asset
Expand Down Expand Up @@ -514,8 +513,21 @@ export function load(asset, onload, onerror) {
initParsers();
}

// transform the url if necessary
if (typeof baseURL[asset.type] !== "undefined") {
// strip url() wrapper for fontface assets so baseURL can be prepended to the raw path
if (asset.type === "fontface" && typeof asset.src === "string") {
const urlMatch = asset.src.match(/^url\(\s*['"]?(.*?)['"]?\s*\)$/);
if (urlMatch) {
asset.src = urlMatch[1];
}
}

// transform the url if necessary (skip for local() font sources and data URIs)
if (
typeof baseURL[asset.type] !== "undefined" &&
typeof asset.src === "string" &&
!asset.src.startsWith("local(") &&
!asset.src.startsWith("data:")
) {
asset.src = baseURL[asset.type] + asset.src;
}

Expand Down
12 changes: 5 additions & 7 deletions packages/melonjs/src/loader/parsers/fontface.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { isDataUrl } from "../../utils/string.ts";
import { fontList } from "../cache.js";

/**
Expand All @@ -10,7 +9,7 @@ import { fontList } from "../cache.js";
* @ignore
* @example
* preloadFontFace(
* name: "'kenpixel'", type: "fontface", src: "url('data/font/kenvector_future.woff2')"
* name: "'kenpixel'", type: "fontface", src: "data/font/kenvector_future.woff2"
* ]);
*/
export function preloadFontFace(data, onload, onerror) {
Expand All @@ -19,11 +18,10 @@ export function preloadFontFace(data, onload, onerror) {
? globalThis.document.fonts
: undefined;

if (isDataUrl(data.src) === true) {
// make sure it in the `url(data:[<mediatype>][;base64],<data>)` format as expected by FontFace
if (!data.src.startsWith("url(")) {
data.src = "url(" + data.src + ")";
}
// FontFace constructor expects src in `url(...)` or `local()` format
// only wrap plain paths in url(); leave url(), local(), and data URIs as-is
if (!data.src.startsWith("url(") && !data.src.startsWith("local(")) {
data.src = "url(" + data.src + ")";
}

if (typeof fontFaceSet !== "undefined") {
Expand Down
60 changes: 60 additions & 0 deletions packages/melonjs/tests/loader.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,71 @@ describe("loader", () => {
expect(loader.baseURL["json"]).toBe("https://cdn.example.com/");
expect(loader.baseURL["binary"]).toBe("https://cdn.example.com/");
expect(loader.baseURL["tmx"]).toBe("https://cdn.example.com/");
expect(loader.baseURL["fontface"]).toBe("https://cdn.example.com/");

// reset
loader.setBaseURL("*", "./");
});

it("should strip url() wrapper from fontface src before applying baseURL", () => {
loader.setBaseURL("fontface", "assets/");
const receivedSrc = [];

// stub fontface parser to capture the resolved src
loader.setParser("fontface", (data, onload) => {
receivedSrc.push(data.src);
if (typeof onload === "function") {
onload();
}
return 1;
});

// plain path
loader.load(
{ name: "font1", type: "fontface", src: "font/test.woff2" },
() => {},
);
// url() wrapped
loader.load(
{ name: "font2", type: "fontface", src: "url(font/test.woff2)" },
() => {},
);
// url() with quotes
loader.load(
{ name: "font3", type: "fontface", src: "url('font/test.woff2')" },
() => {},
);

// all should resolve to the same baseURL + path
expect(receivedSrc[0]).toBe("assets/font/test.woff2");
expect(receivedSrc[1]).toBe("assets/font/test.woff2");
expect(receivedSrc[2]).toBe("assets/font/test.woff2");

Comment on lines +273 to +306
// reset
loader.setBaseURL("fontface", "./");
});

it("should not strip local() wrapper from fontface src", () => {
// reset baseURL so it doesn't interfere
loader.setBaseURL("fontface", "./");
const receivedSrc = [];

loader.setParser("fontface", (data, onload) => {
receivedSrc.push(data.src);
if (typeof onload === "function") {
onload();
}
return 1;
});

loader.load(
{ name: "font4", type: "fontface", src: "local('My Font')" },
() => {},
);

expect(receivedSrc[0]).toBe("local('My Font')");
});

it("should configure loader options", () => {
loader.setOptions({
crossOrigin: "use-credentials",
Expand Down
Loading