我正在做一个原始的波浪动画。但是遇到了问题。第一条贝塞尔曲线与后面的不一致,因此,我不得不将这部分隐藏在画布后面。
您如何简化动画,因为现在每次都会创建新路径?
我怀疑可以绘制一个元素,一个半波,例如,它可以使用模式重复。但是波浪的高度、宽度以及未来的坡度可以改变。这就是为什么我不想让这个瓷砖“在石头上”。您如何使这个重复元素可停靠并同时以编程方式更改?可以用 SVG 和 CSS 动画解决吗?
开头隐藏波浪的示例:
const SPEED = 0.05;
const WAVE_WIDTH = 100;
const WAVE_HEIGHT = 50;
const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 200;
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
let w = canvas.width = CANVAS_WIDTH;
let h = canvas.height = CANVAS_HEIGHT;
ctx.fillStyle = 'blue';
let wave_fase = 0;
draw();
function draw(){
ctx.clearRect(0,0,w,h);
let waves_path_string = `M ${WAVE_WIDTH*(wave_fase - 3)} ${h}
V ${h/2 - WAVE_HEIGHT/2}
q ${WAVE_WIDTH/2} ${-WAVE_HEIGHT/2} ${WAVE_WIDTH} ${WAVE_HEIGHT/2} `;
waves_path_string += 't' + ` ${WAVE_WIDTH} 0`.repeat(w/WAVE_WIDTH + 3) + ` V ${h} z`;
ctx.fill(new Path2D(waves_path_string));
wave_fase = (wave_fase + SPEED)%2;
requestAnimationFrame(draw);
}
<canvas></canvas>
在这里你可以看到第一波,它不想融入一般行。
const SPEED = 0.01;
const WAVE_WIDTH = 60;
const WAVE_HEIGHT = 40;
const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 200;
let canvas = document.querySelector('canvas');
let ctx = canvas.getContext('2d');
let w = canvas.width = CANVAS_WIDTH;
let h = canvas.height = CANVAS_HEIGHT;
ctx.fillStyle = 'blue';
let wave_fase = 0;
draw();
function draw(){
ctx.clearRect(0,0,w,h);
let waves_path_string = `M ${WAVE_WIDTH*wave_fase} ${h}
V ${h/2 - WAVE_HEIGHT/2}
q ${WAVE_WIDTH/2} ${-WAVE_HEIGHT/2} ${WAVE_WIDTH} ${WAVE_HEIGHT/2} `;
waves_path_string += 't' + ` ${WAVE_WIDTH} 0`.repeat(w/WAVE_WIDTH) + ` V ${h} z`;
ctx.fill(new Path2D(waves_path_string));
wave_fase = (wave_fase + SPEED)%2;
requestAnimationFrame(draw);
}
<canvas></canvas>
这是相同的 SVG 代码,属性操作d
位于<path>
:
const SPEED = 0.05;
const WAVE_WIDTH = 100;
const WAVE_HEIGHT = 50;
const CANVAS_WIDTH = 600;
const CANVAS_HEIGHT = 200;
let svg = document.querySelector('svg');
svg.setAttribute('width',CANVAS_WIDTH);
svg.setAttribute('height',CANVAS_WIDTH);
let wave_fase = 0;
draw();
function draw(){
let waves_path_string = `M ${WAVE_WIDTH*(wave_fase - 3)} ${CANVAS_HEIGHT}
V ${CANVAS_HEIGHT/2 - WAVE_HEIGHT/2}
q ${WAVE_WIDTH/2} ${-WAVE_HEIGHT/2} ${WAVE_WIDTH} ${WAVE_HEIGHT/2} `;
waves_path_string += 't' + ` ${WAVE_WIDTH} 0`.repeat(CANVAS_WIDTH/WAVE_WIDTH + 3) + ` V ${CANVAS_HEIGHT} z`;
svg.querySelector('path').setAttribute('d', waves_path_string);
wave_fase = (wave_fase + SPEED)%2;
requestAnimationFrame(draw);
}
<svg>
<path d="" fill="blue"/>
</svg>
试试这个方法,看起来更像波浪。
下面是完整的代码:
基于 @Andy Fitzsimon 在codepen上的主题并重新设计
更新
根据评论中讨论的结果,我意识到问题的作者需要某种通用解决方案,无需计算任何内容,只需更改输入数据即可获得结果。这是 Q的好答案中的@Stranger
我希望这有助于解决问题。
我喜欢作者的方法。为什么第一条曲线与后面的曲线不吻合?因为它
q
设置了相对坐标,并且第一个点在波的顶部。随后的“半周期”相对于中线完成。那些。事实证明,第一个山脊的初始点和控制点被拉高了。当相邻的控制点是彼此的镜像时,贝塞尔曲线非常适合,@Leonid 的解决方案通过 实现了
t
这一点,这就是该方法的美妙之处。一个小的调整解决了第一个梳子的问题:
为了不在 draw() 函数中每次都创建路径,可以构建一次路径并使用“循环”
translate()
,但我没有尝试过。我在 CSS 上为你弥补了“茶杯中的风暴”: