动画正弦波,固定起点和终点。

9
我在画布上有一个正弦波,正在进行动画播放,左右摇摆。我想要实现的是始点和终点保持不变。如何实现这个目标?
这里是Code Pen

function start() {
  var canvas = document.getElementById("canvas");
  var context = canvas.getContext("2d");
  context.clearRect(0, 0, canvas.width, canvas.height);
  drawCurves(context, step);

  step += 5;
  window.requestAnimationFrame(start);
}

var step = -4;

function drawCurves(ctx, step) {
  var width = ctx.canvas.width;
  var height = ctx.canvas.height;
  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.strokeStyle = "rgb(66,44,255)";

  var x = 4;
  var y = 0;
  var amplitude = 20;
  var frequency = 90;
  while (y < height) {
    x = width / 2 + amplitude * Math.sin((y + step) / frequency);
    ctx.lineTo(x, y);
    y++;
  }
  ctx.stroke();
}
canvas {
  background-color: wheat;
}
<!DOCTYPE html>
<html>

<head>
</head>

<body onload="start()">

  <canvas id="canvas" width="500" height="2000"></canvas>

</body>

</html>

2个回答

10
我更改了你的画布大小,因为我想要能够看到它。你可以将其改回你需要的大小。
我做了两件事:
  1. 频率必须是 var frequency = height / (2 * Math.PI); 或者 var frequency = height / (4 * Math.PI);。除数必须是 2 * Math.PI 的倍数。
  2. 我将上下文翻转了同样的量:ctx.translate(-amplitude * Math.sin(step / frequency), 0);
如果你需要更细微的振荡,请调整振幅。在我的代码中,有注释掉的 ctx.closePath(),请取消注释以清楚地看到正弦波停留在中心。希望这就是你所问的内容。
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");


function start() {

  context.clearRect(0, 0, canvas.width, canvas.height);
  drawCurves(context, step);

  step += 5;
  window.requestAnimationFrame(start);
}

var step = -4;

function drawCurves(ctx, step) {
  var width = ctx.canvas.width;
  var height = ctx.canvas.height;
  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.strokeStyle = "rgb(66,44,255)";

  var x = 0;
  var y = 0;
  var amplitude = 10;
  var frequency = height / (2 * Math.PI);
  ctx.save();
  ctx.translate(-amplitude * Math.sin(step / frequency), 0);
  while (y < height) {
    x = width / 2 + amplitude * Math.sin((y + step) / frequency);
    ctx.lineTo(x, y);
    y++;
  }
  //ctx.closePath();
  ctx.stroke();
  ctx.restore();
}

start();
canvas {
  background-color: wheat;
}

div {
  width: 100px;
  height: 400px;
  border: solid;
}
<div class="box">
<canvas id="canvas" width="100" height="400"></canvas>
</div>

更新

如果你需要使用多个曲线,你可以像这样做:

我将绘制波形的所有功能放在一个名为drawWave的函数中,该函数以振幅和要使用的三角函数(正弦或余弦)作为参数:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
var width = ctx.canvas.width;
var height = ctx.canvas.height;
var step = -4;

function start() {
  window.requestAnimationFrame(start);
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  
  drawWave(10,"sin");
  drawWave(10,"cos");
  drawWave(5,"sin");
  
  step += 5; 
}


  
function drawWave(amplitude,trig){
  // trig is the trigonometric function to be used: sin or cos
  ctx.beginPath();
  ctx.lineWidth = 2;
  ctx.strokeStyle = "rgb(66,44,255)";

  var x = 0;
  var y = 0;
  //var amplitude = 10;
  var frequency = height / (2 * Math.PI);
  ctx.save();
  ctx.translate(-amplitude * Math[trig](step / frequency), 0);
  while (y < height) {
    x = width / 2 + amplitude * Math[trig]((y + step) / frequency);
    ctx.lineTo(x, y);
    y++;
  }

  ctx.stroke();
  ctx.restore();
}

start();
canvas {
  background-color: wheat;
}

div {
  width: 100px;
  height: 400px;
  border: solid;
}
<div class="box">
<canvas id="canvas" width="100" height="400"></canvas>
</div>


谢谢,这就是我想问的!也许你可以帮我更进一步,我试图实现的最终效果是这样的(https://riteshkukreja.github.io/sine-wave/)。目前所有的波浪都在做完全相同的事情,我很难改变每个波浪的速度和方向!您可以在我提供的 Code Pen 中看到更新。谢谢! - felixo
天才!有没有一种方法可以为closePath()添加样式,比如不同的宽度和颜色? - felixo
closePath()? I'm not sure I understand you. If you want to have different color waves you can add a third argument to the drawWave function: the color. And you can use this color for the strokeStyle - enxaneta
波浪看起来不错!但如果我加上closePath(),它会从顶部绘制一条直线到底部右侧?那就是我想更改样式的那条线!听起来合理吗? - felixo
在这种情况下,对于直线,您可以添加另一个振幅为0的波形: - enxaneta
显示剩余2条评论

4

GLSL版本

由于此片段着色器中的UV坐标从0到1变化,因此达成目标非常简单,您只需要一个可以被π整除的波浪频率。

let gl = canvas.getContext('webgl');
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1,  3, -1, -1, 3, -1]), gl.STATIC_DRAW);

let pid = gl.createProgram();
shader(`attribute vec2 v;void main(void){gl_Position=vec4(v.xy,0.,1.);}`,gl.VERTEX_SHADER);
shader(document.querySelector(`script[type="glsl"]`).textContent,gl.FRAGMENT_SHADER);
gl.linkProgram(pid);
gl.useProgram(pid);

let v = gl.getAttribLocation(pid, "v");
gl.vertexAttribPointer(v, 2, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(v);

let resolution = gl.getUniformLocation(pid, 'resolution');
let time = gl.getUniformLocation(pid, 'time');

requestAnimationFrame(draw);

function draw(t) {
  gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.clearColor(0, 0, 0, 0);
  gl.uniform1f(time, t/500);
  gl.uniform2f(resolution, gl.drawingBufferWidth, gl.drawingBufferHeight);
  gl.drawArrays(gl.TRIANGLES, 0, 3);
  requestAnimationFrame(draw);
}

function shader(src, type) {
  let sid = gl.createShader(type);
  gl.shaderSource(sid, src);
  gl.compileShader(sid);
  gl.attachShader(pid, sid);
}
<canvas width="200" height="600" id="canvas"/>
<script type="glsl">
precision highp float;
uniform float time;
uniform vec2 resolution;

void main(void) {
  vec2 uv = gl_FragCoord.xy / resolution.xy;
  vec2 p = 20.*uv - 10.;
  vec3 f = vec3(0.);
  f+=pow(abs(p.x+5.*sin(time*2.)*sin(uv.y*6.28*2.)+cos(uv.y*11.)),-0.8)*vec3(.5,.0,.5);
  f+=pow(abs(p.x+4.*sin(time*3.)*sin(uv.y*6.28*.5)+cos(uv.y*21.)),-0.8)*vec3(.5,.5,.0);
  f+=pow(abs(p.x+3.*sin(time*5.)*sin(uv.y*6.28*1.)+cos(uv.y*17.)),-0.8)*vec3(.0,.5,.5);
  gl_FragColor = vec4(f, 1.);  
}
</script>


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接