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);
};
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') {
return this;
}
if (window.strudelAnimation) {
cancelAnimationFrame(window.strudelAnimation);
if (animationFrames[id]) {
cancelAnimationFrame(animationFrames[id]);
}
ctx = ctx || getDrawContext();
let cycle,
@ -56,7 +57,7 @@ Pattern.prototype.draw = function (callback, { from, to, onQuery, ctx } = {}) {
}
}
callback(ctx, events, t, time);
window.strudelAnimation = requestAnimationFrame(animate);
animationFrames[id] = requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
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
// 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') {
return this;
}
if (window.strudelAnimation) {
cancelAnimationFrame(window.strudelAnimation);
if (animationFrames[id]) {
cancelAnimationFrame(animationFrames[id]);
}
const animate = () => {
const t = getTime() + offset;
const haps = this.queryArc(t, t);
fn(haps, t, this);
window.strudelAnimation = requestAnimationFrame(animate);
animationFrames[id] = requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
return this;
@ -84,9 +85,7 @@ Pattern.prototype.onFrame = function (fn, offset = 0) {
export const cleanupDraw = (clearScreen = true) => {
const ctx = getDrawContext();
clearScreen && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.width);
if (window.strudelAnimation) {
cancelAnimationFrame(window.strudelAnimation);
}
Object.values(animationFrames).forEach((id) => cancelAnimationFrame(id));
if (window.strudelScheduler) {
clearInterval(window.strudelScheduler);
}

View File

@ -30,7 +30,7 @@ const getValue = (e) => {
};
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 to = cycles * (1 - playhead);
@ -50,6 +50,7 @@ Pattern.prototype.pianoroll = function (options = {}) {
from: from - overscan,
to: to + overscan,
ctx,
id,
},
);
return this;

View File

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