support multiple animationFrames

+ break out spiral draw logic
This commit is contained in:
Felix Roos 2024-03-15 09:49:00 +01:00
parent a8712fd8ce
commit 6dce9d5deb
3 changed files with 58 additions and 54 deletions

View File

@ -29,12 +29,13 @@ export const getDrawContext = (id = 'test-canvas', options) => {
return canvas.getContext(contextType); return canvas.getContext(contextType);
}; };
Pattern.prototype.draw = function (callback, { from, to, onQuery, ctx } = {}) { let animationFrames = {};
Pattern.prototype.draw = function (callback, { id = 'std', from, to, onQuery, ctx } = {}) {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
return this; return this;
} }
if (window.strudelAnimation) { if (animationFrames[id]) {
cancelAnimationFrame(window.strudelAnimation); cancelAnimationFrame(animationFrames[id]);
} }
ctx = ctx || getDrawContext(); ctx = ctx || getDrawContext();
let cycle, let cycle,
@ -56,7 +57,7 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery, ctx } = {}) {
} }
} }
callback(ctx, events, t, time); callback(ctx, events, t, time);
window.strudelAnimation = requestAnimationFrame(animate); animationFrames[id] = requestAnimationFrame(animate);
}; };
requestAnimationFrame(animate); requestAnimationFrame(animate);
return this; return this;
@ -64,18 +65,18 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery, ctx } = {}) {
// this is a more generic helper to get a rendering callback for the currently active haps // this is a more generic helper to get a rendering callback for the currently active haps
// TODO: this misses events that are prolonged with clip or duration (would need state) // TODO: this misses events that are prolonged with clip or duration (would need state)
Pattern.prototype.onFrame = function (fn, offset = 0) { Pattern.prototype.onFrame = function (id, fn, offset = 0) {
if (typeof window === 'undefined') { if (typeof window === 'undefined') {
return this; return this;
} }
if (window.strudelAnimation) { if (animationFrames[id]) {
cancelAnimationFrame(window.strudelAnimation); cancelAnimationFrame(animationFrames[id]);
} }
const animate = () => { const animate = () => {
const t = getTime() + offset; const t = getTime() + offset;
const haps = this.queryArc(t, t); const haps = this.queryArc(t, t);
fn(haps, t, this); fn(haps, t, this);
window.strudelAnimation = requestAnimationFrame(animate); animationFrames[id] = requestAnimationFrame(animate);
}; };
requestAnimationFrame(animate); requestAnimationFrame(animate);
return this; return this;
@ -84,9 +85,7 @@ Pattern.prototype.onFrame = function (fn, offset = 0) {
export const cleanupDraw = (clearScreen = true) => { export const cleanupDraw = (clearScreen = true) => {
const ctx = getDrawContext(); const ctx = getDrawContext();
clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.width); clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.width);
if (window.strudelAnimation) { Object.values(animationFrames).forEach((id) => cancelAnimationFrame(id));
cancelAnimationFrame(window.strudelAnimation);
}
if (window.strudelScheduler) { if (window.strudelScheduler) {
clearInterval(window.strudelScheduler); clearInterval(window.strudelScheduler);
} }

View File

@ -30,7 +30,7 @@ const getValue = (e) => {
}; };
Pattern.prototype.pianoroll = function (options = {}) { Pattern.prototype.pianoroll = function (options = {}) {
let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false, ctx } = options; let { cycles = 4, playhead = 0.5, overscan = 1, hideNegative = false, ctx, id } = options;
let from = -cycles * playhead; let from = -cycles * playhead;
let to = cycles * (1 - playhead); let to = cycles * (1 - playhead);
@ -50,6 +50,7 @@ Pattern.prototype.pianoroll = function (options = {}) {
from: from - overscan, from: from - overscan,
to: to + overscan, to: to + overscan,
ctx, ctx,
id,
}, },
); );
return this; return this;

View File

@ -49,7 +49,7 @@ function spiralSegment(options) {
ctx.stroke(); ctx.stroke();
} }
Pattern.prototype.spiral = function (options = {}) { function drawSpiral(options) {
const { const {
stretch = 1, stretch = 1,
size = 80, size = 80,
@ -65,54 +65,58 @@ Pattern.prototype.spiral = function (options = {}) {
colorizeInactive = 0, colorizeInactive = 0,
fade = true, fade = true,
// logSpiral = true, // logSpiral = true,
ctx,
time,
haps,
drawTime,
} = options; } = options;
function spiral({ ctx, time, haps, drawTime }) { const [w, h] = [ctx.canvas.width, ctx.canvas.height];
const [w, h] = [ctx.canvas.width, ctx.canvas.height]; ctx.clearRect(0, 0, w * 2, h * 2);
ctx.clearRect(0, 0, w * 2, h * 2); const [cx, cy] = [w / 2, h / 2];
const [cx, cy] = [w / 2, h / 2]; const settings = {
const settings = { margin: size / stretch,
margin: size / stretch, cx,
cx, cy,
cy, stretch,
stretch, cap,
cap, thickness,
thickness, };
};
const playhead = { const playhead = {
...settings, ...settings,
thickness: playheadThickness, thickness: playheadThickness,
from: inset - playheadLength, from: inset - playheadLength,
to: inset, to: inset,
color: playheadColor, color: playheadColor,
}; };
const [min] = drawTime; const [min] = drawTime;
const rotate = steady * time; const rotate = steady * time;
haps.forEach((hap) => { haps.forEach((hap) => {
const isActive = hap.whole.begin <= time && hap.endClipped > time; const isActive = hap.whole.begin <= time && hap.endClipped > time;
const from = hap.whole.begin - time + inset; const from = hap.whole.begin - time + inset;
const to = hap.endClipped - time + inset - padding; const to = hap.endClipped - time + inset - padding;
const { color } = hap.context; const { color } = hap.context;
const opacity = fade ? 1 - Math.abs((hap.whole.begin - time) / min) : 1; const opacity = fade ? 1 - Math.abs((hap.whole.begin - time) / min) : 1;
spiralSegment({
ctx,
...settings,
from,
to,
rotate,
color: colorizeInactive || isActive ? color : inactiveColor,
fromOpacity: opacity,
toOpacity: opacity,
});
});
spiralSegment({ spiralSegment({
ctx, ctx,
...playhead, ...settings,
from,
to,
rotate, rotate,
color: colorizeInactive || isActive ? color : inactiveColor,
fromOpacity: opacity,
toOpacity: opacity,
}); });
} });
spiralSegment({
ctx,
...playhead,
rotate,
});
}
return this.onPaint((ctx, time, haps, drawTime) => spiral({ ctx, time, haps, drawTime })); Pattern.prototype.spiral = function (options = {}) {
return this.onPaint((ctx, time, haps, drawTime) => drawSpiral({ ctx, time, haps, drawTime, ...options }));
}; };