diff --git a/packages/draw/draw.mjs b/packages/draw/draw.mjs index 63245baf..910ac08c 100644 --- a/packages/draw/draw.mjs +++ b/packages/draw/draw.mjs @@ -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); } diff --git a/packages/draw/pianoroll.mjs b/packages/draw/pianoroll.mjs index b032ef1a..29c61d18 100644 --- a/packages/draw/pianoroll.mjs +++ b/packages/draw/pianoroll.mjs @@ -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; diff --git a/packages/draw/spiral.mjs b/packages/draw/spiral.mjs index 00bd62ec..4762e843 100644 --- a/packages/draw/spiral.mjs +++ b/packages/draw/spiral.mjs @@ -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 })); };