Skip to content

Commit

Permalink
Preload area spanning animation
Browse files Browse the repository at this point in the history
  • Loading branch information
darthmaim committed Jul 9, 2024
1 parent 640f984 commit 718f4e3
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 14 deletions.
2 changes: 2 additions & 0 deletions packages/tyria/dev/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<button id="lionsarch">Lion's Arch</button>
<button id="ascalon">Ascalon</button>
<button id="horn">Horn of Maguuma</button>
<button id="cantha">Cantha</button>
</div>
<script type="module">
import { Tyria, TileLayer } from './dist/index.mjs';
Expand All @@ -98,6 +99,7 @@
document.getElementById('lionsarch').addEventListener('click', () => map.easeTo({ contain: [[48130, 30720], [50430, 32250]] }))
document.getElementById('ascalon').addEventListener('click', () => map.easeTo({ contain: [[56682, 24700], [64500, 35800]], zoom: 3 }))
document.getElementById('horn').addEventListener('click', () => map.easeTo({ contain: [[19328, 19048], [27296, 24800]] }))
document.getElementById('cantha').addEventListener('click', () => map.easeTo({ contain: [[20576, 97840], [39056, 106256]] }))

window.map = map;
</script>
Expand Down
37 changes: 30 additions & 7 deletions packages/tyria/src/Tyria.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { HandlerManager } from './handlers/manager';
import { ImageManager } from './image-manager';
import { Layer, LayerPreloadContext, LayerRenderContext } from './layer';
import { TyriaMapOptions } from './options';
import { RenderQueue, RenderQueuePriority } from './render-queue';
import { Point, View, ViewOptions } from './types';
import { RenderQueue, RenderQueuePriority, RenderReason } from './render-queue';
import { Bounds, Point, View, ViewOptions } from './types';
import { add, clamp, easeInOutCubic, multiply, subtract } from './util';

export class Tyria {
Expand Down Expand Up @@ -79,11 +79,11 @@ export class Tyria {
return [x * nativeZoomScale / zoomScale, y * nativeZoomScale / zoomScale];
}

queueRender(priority?: RenderQueuePriority) {
this.renderQueue.queue(priority);
queueRender(priority?: RenderQueuePriority, reason?: RenderReason) {
this.renderQueue.queue(priority, reason);
}

#render() {
#render(reason: RenderReason) {
// we are doing it, cancel any pending renders
this.renderQueue.cancel();

Expand All @@ -100,6 +100,8 @@ export class Tyria {
// preload images
this.#preloadImages();

console.log('render', reason);

performance.mark('render-start', { detail: { view: this.view }});

// calculate the global transform of the map
Expand All @@ -125,6 +127,7 @@ export class Tyria {
dpr,
debug: this.debug,
},
reason,
project: this.project.bind(this),
unproject: this.unproject.bind(this),
}
Expand Down Expand Up @@ -311,6 +314,18 @@ export class Tyria {
return { center, zoom };
}

/** Gets the area visible in the viewport */
#getViewportArea(view: View) {
const dpr = window.devicePixelRatio ?? 1;
const viewportHalfSizePx: Point = [this.canvas.width / dpr / 2, this.canvas.height / dpr / 2];
const centerPx = this.project(view.center);

const topLeft = this.unproject(subtract(centerPx, viewportHalfSizePx));
const bottomRight = this.unproject(add(centerPx, viewportHalfSizePx));

return [topLeft, bottomRight];
}

/** Instantly jumps to the provided view */
jumpTo(view: ViewOptions) {
this.debugLastViewOptions = view;
Expand Down Expand Up @@ -344,6 +359,14 @@ export class Tyria {
// preload target view
this.preload(target);

const startArea = this.#getViewportArea(start);
const targetArea = this.#getViewportArea(target);
const combinedArea: Bounds = [
[Math.min(startArea[0][0], targetArea[0][0]), Math.min(startArea[0][1], targetArea[0][1])] as Point,
[Math.max(startArea[1][0], targetArea[1][0]), Math.max(startArea[1][1], targetArea[1][1])] as Point
];
this.preload(this.resolveView({ contain: combinedArea }));

// if we are not moving, don't move
if(target.zoom === start.zoom && target.center[0] === start.center[0] && target.center[1] === start.center[1]) {
return;
Expand Down Expand Up @@ -393,7 +416,7 @@ export class Tyria {
this.currentEase = { frame, duration, start: performance.now() }
}

this.queueRender();
this.queueRender('next-frame', 'ease');
}

/** The currently running easing */
Expand All @@ -419,7 +442,7 @@ export class Tyria {
// if the animation is not yet finished, queue another frame,
// otherwise unset the current one
if(progress < 1) {
this.queueRender();
this.queueRender('next-frame', 'ease');
} else {
this.currentEase = undefined;
}
Expand Down
4 changes: 3 additions & 1 deletion packages/tyria/src/layer.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ImageGetOptions } from "./image-manager";
import { RenderReason } from "./render-queue";
import { Point } from "./types";

export interface Layer {
Expand All @@ -9,6 +10,7 @@ export interface Layer {

export interface LayerRenderContext {
context: CanvasRenderingContext2D,
reason: RenderReason,
state: {
/** center of the map in map coordinates */
center: Point,
Expand All @@ -33,4 +35,4 @@ export interface LayerRenderContext {
getImage: (src: string, options?: ImageGetOptions) => ImageBitmap | undefined,
}

export type LayerPreloadContext = Omit<LayerRenderContext, 'context'>;
export type LayerPreloadContext = Omit<LayerRenderContext, 'context' | 'reason'>;
4 changes: 2 additions & 2 deletions packages/tyria/src/layers/TileLayer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class TileLayer implements Layer {
}
}

render({ context, state, project, getImage }: LayerRenderContext) {
render({ context, state, project, getImage, reason }: LayerRenderContext) {
performance.mark('tile-layer-render-start');

// get tiles in viewport
Expand Down Expand Up @@ -114,7 +114,7 @@ export class TileLayer implements Layer {

// try to get the tile from the cache
const src = this.options.source(x, y, zoom);
const tile = getImage(src, { priority: 2 - distance });
const tile = getImage(src, { priority: 2 - distance, cacheOnly: reason === 'ease' });

if(tile) {
// draw tile
Expand Down
13 changes: 9 additions & 4 deletions packages/tyria/src/render-queue.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
export type RenderQueuePriority = 'next-frame' | 'low-priority';

export type RenderReason = 'render' | 'ease';

export class RenderQueue {
#render: () => void;
#render: (reason: RenderReason) => void;
#renderQueued: false | RenderQueuePriority = false;
#renderQueueFrame?: number;
#renderQueueTimeout?: number;
#reason: RenderReason;

constructor(render: () => void) {
this.#render = render;
}

queue(priority: RenderQueuePriority = 'next-frame') {
queue(priority: RenderQueuePriority = 'next-frame', reason: RenderReason = 'render') {
// don't queue if it is already queued with same or higher priority
if(this.#renderQueued === priority || this.#renderQueued === 'next-frame') {
return;
}

this.#reason = reason;

if(priority === 'next-frame') {
// cancel low priority request if we get a high priority one
if(this.#renderQueued === 'low-priority') {
Expand All @@ -25,13 +30,13 @@ export class RenderQueue {
// request render in next animation frame
this.#renderQueueFrame = requestAnimationFrame(() => {
this.#renderQueueFrame = undefined;
this.#render();
this.#render(this.#reason);
});
} else {
// render in 80ms (5 frames at ~60fps), so we can collect some more queueRenders until then (for example from image loading promises resolving)
this.#renderQueueTimeout = setTimeout(() => {
this.#renderQueueTimeout = undefined;
this.#render();
this.#render(this.#reason);
}, 80);
}

Expand Down

0 comments on commit 718f4e3

Please sign in to comment.