У меня есть синусоидальная волна на моем холсте, которая анимирована и колеблется влево и вправо. Я пытаюсь добиться того, чтобы начальная и конечная точки оставались фиксированными. Как этого добиться?
Вот Кодовая ручка
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>


![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я изменил размер вашего холста, потому что хотел его увидеть. Вы можете изменить его обратно на то, что вам нужно.
Я сделал 2 вещи:
Частота должна быть var frequency = height / (2 * Math.PI); или var frequency = height / (4 * Math.PI);. Делитель должен быть кратен 2 * Math.PI.
Я перевожу контекст в обратном направлении столько же: 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, которая принимает в качестве аргументов амплитуду и тригонометрическую функцию (sin или cos):
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>Гений! Есть ли способ стилизовать closePath(), например, добавить другую ширину и цвет?
closePath()? Я не уверен, что понимаю тебя. Если вы хотите иметь разные цветовые волны, вы можете добавить третий аргумент в функцию drawWave: цвет. И вы можете использовать этот цвет для strokeStyleВолны в порядке! Но если я добавлю closePath(), он нарисует прямую линию сверху вниз справа? И это линия, где я хотел бы изменить стиль! Имеет ли это смысл?
В этом случае для прямой линии вы можете добавить еще одну волну с амплитудой = 0:
Спасибо за вклад, я понял, что только усложняю, когда просто реализовал то, что мне нужно, с помощью элемента ::after! Я заметил с вашим решением, что с помощью шага скорость волн меняется в зависимости от производительности устройства! Теперь я пытаюсь и задаюсь вопросом, можно ли это сделать с временной меткой requestAnimationFrame (developer.mozilla.org/en-US/docs/Web/API/window/…), поэтому она постоянна на всех устройствах!
Я использовал ваш код, включая шаг. requestAnimationFrame timestamp будет другим вопросом
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>
спасибо, это то, что я спрашивал! Может быть, вы можете помочь мне в дальнейшем, конечный эффект, которого я пытаюсь достичь, таков (riteshkukreja.github.io/sine-wave). На данный момент все волны делают одно и то же, у меня проблемы с изменением скорости и направления каждой волны! Вы можете увидеть обновления в моем Code Pen, предоставленном в начальном вопросе. Спасибо!