如何在P5JS中实现绘制动画倒放,完成多幅预设艺术作品循环切换效果?
p5.js 多艺术作品循环切换+倒放擦除效果实现
核心思路
要实现绘制、停留、倒放擦除、循环切换的效果,核心是把每个作品的绘制过程拆解为独立的帧步骤,而非一次性渲染完成,倒放时只需要反向执行绘制逻辑/回退到上一帧状态即可。
实现代码
// 全局状态配置 const CANVAS_W = 800, CANVAS_H = 1200; // 海报尺寸可自定义 const STAY_FRAMES = 180; // 作品停留时长,60帧约等于1秒,180即停留3秒 const WORK_COUNT = 3; // 预设作品总数 let works = []; // 存储所有作品的绘制帧序列 let currentWork = 0; let currentFrameIdx = 0; let runState = "draw"; // 运行状态:draw正向绘制 / stay停留 / erase倒放擦除 / switch切换作品 let stayTimer = 0; function preload() { // 预生成所有作品的绘制帧序列 for (let i = 0; i < WORK_COUNT; i++) { works.push(createWorkFrames(i)); } } function setup() { createCanvas(CANVAS_W, CANVAS_H); background(255); strokeWeight(2); } function draw() { switch(runState) { case "draw": // 执行当前帧的绘制操作 works[currentWork][currentFrameIdx](); currentFrameIdx++; // 绘制完成切换到停留状态 if (currentFrameIdx >= works[currentWork].length) { runState = "stay"; stayTimer = 0; } break; case "stay": stayTimer++; if (stayTimer >= STAY_FRAMES) { runState = "erase"; } break; case "erase": // 倒放:重绘到上一帧的状态 background(255); currentFrameIdx--; for (let i = 0; i < currentFrameIdx; i++) { works[currentWork][i](); } // 擦除完成切换到作品切换状态 if (currentFrameIdx <= 0) { runState = "switch"; } break; case "switch": // 循环切换到下一个作品 currentWork = (currentWork + 1) % WORK_COUNT; currentFrameIdx = 0; runState = "draw"; break; } } /** * 生成单个作品的绘制帧序列 * @param {number} workId 作品编号 0/1/2 * @returns {Array<Function>} 每一步绘制操作的函数数组 */ function createWorkFrames(workId) { const frames = []; switch(workId) { case 0: // 作品1示例:随机斜线 for (let i = 0; i < 120; i++) { const x1 = random(CANVAS_W), y1 = random(CANVAS_H); const x2 = random(CANVAS_W), y2 = random(CANVAS_H); frames.push(() => { stroke(0); line(x1, y1, x2, y2); }); } break; case 1: // 作品2示例:彩色圆形 for (let i = 0; i < 90; i++) { const x = random(CANVAS_W), y = random(CANVAS_H); const r = random(20, 70); const fillColor = color(random(255), random(80, 180), random(200)); frames.push(() => { noStroke(); fill(fillColor); circle(x, y, r); }); } break; case 2: // 作品3示例:网格色块 for (let x = 0; x < CANVAS_W; x += 40) { for (let y = 0; y < CANVAS_H; y += 40) { const gray = random(50, 220); frames.push(() => { noStroke(); fill(gray); rect(x, y, 38, 38); }); } } break; } return frames; }
优化提示
- 如果作品绘制步骤较多,倒放时逐帧重绘性能不足,可以每间隔5-10帧用
get()方法存储画布快照,倒放时直接调用image()渲染对应快照即可,运行效率会大幅提升 - 可以在擦除完成后加几帧全白停留,避免作品切换时出现视觉跳变
内容的提问来源于stack exchange,提问作者Mr Loosid
相关产品推荐
相关产品推荐

