如何使JavaScript中的正弦波正常工作?

问题描述

我正在尝试在画布上制作正弦波,该画布会被填充某种颜色。我成功创建了wave动画。但是,我似乎无法正确地填补空缺。您可以在下面的代码段中看到我的代码的工作方式

const canvas = document.querySelector('canvas')
const gui = new dat.GUI()
const c = canvas.getContext('2d')
canvas.width = innerWidth
canvas.height = innerHeight

const wave = {
  y: canvas.height / 2,length: 0.01,amplitude: 150,frequency: 0.01
}

const strokeColor = {
  h: 200,s: 50,l: 50
}

const backgroundColor = {
  r: 255,g: 50,b: 255,a: 1
}

const waveFolder = gui.addFolder('wave')
waveFolder.add(wave,'y',canvas.height)
waveFolder.add(wave,'length',-0.01,0.01)
waveFolder.add(wave,'amplitude',-300,300)
waveFolder.add(wave,'frequency',1)

const strokeFolder = gui.addFolder('stroke')
strokeFolder.add(strokeColor,'h',255)
strokeFolder.add(strokeColor,'s',100)
strokeFolder.add(strokeColor,'l',100)

const backgroundFolder = gui.addFolder('background')
backgroundFolder.add(backgroundColor,'r',255)
backgroundFolder.add(backgroundColor,'g',255)

backgroundFolder.add(backgroundColor,'b','a',1)

let increment = 0

function drawSineWave() {
  c.fillStyle = `rgba(${backgroundColor.r},${backgroundColor.g},${backgroundColor.b},${backgroundColor.a})`
  c.clearRect(0,canvas.width,canvas.height)
  increment++
  c.beginPath()

  c.moveto(0,canvas.height / 2)
  c.lineto(0,canvas.height)
  c.lineto(canvas.width,canvas.height / 2)
  c.moveto(0,canvas.height / 2)

  for (let i = 0; i < canvas.width; i++) {
    c.lineto(i,wave.y + Math.sin(increment / 50) * wave.amplitude * Math.sin(i * wave.length + wave.frequency))
  }
  c.lineto(canvas.width,canvas.height / 2)
  c.strokeStyle = `hsl(${strokeColor.h},${strokeColor.s}%,${strokeColor.l}%)`
  c.fill()
  c.stroke()
}

function animate() {
  requestAnimationFrame(animate)
  drawSineWave()
}

animate()
* {
  margin: 0;
  padding: 0;
  Box-sizing: border-Box;
}

canvas {
  width: 100%;
  height: 100%;
}
<head>
  <title>Waves</title>
  <link rel="stylesheet" href="index.css">
</head>

<body>
  <canvas></canvas>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
  <script src="index.js"></script>
</body>

我希望波浪的外观是这样的:

Proper filled sine wave

有人可以帮助我达到预期的结果吗?谢谢!

解决方法

您只需要按正确的顺序绘制下半部分

结果是

function drawSineWave() {
  c.fillStyle = `rgba(${backgroundColor.r},${backgroundColor.g},${backgroundColor.b},${backgroundColor.a})`
  c.clearRect(0,canvas.width,canvas.height)
  increment++
  c.beginPath()
  for (let i = 0; i < canvas.width; i++) {
    c.lineTo(i,wave.y + Math.sin(increment / 50) * wave.amplitude * Math.sin(i * wave.length + wave.frequency))
  }
  c.strokeStyle = `hsl(${strokeColor.h},${strokeColor.s}%,${strokeColor.l}%)`
  c.stroke();  // draw stroke along wave top only

  c.lineTo(canvas.width,canvas.height) // bottom right
  c.lineTo(0,canvas.height)            // bottom left
  c.fill()
}

示例

我玩了一点。

const canvas = document.querySelector('canvas')
const gui = new dat.GUI()
const c = canvas.getContext('2d')
canvas.width = innerWidth
canvas.height = innerHeight

const wave = {
    offset: canvas.height/2,speed: 1,amplitude:50,frequency: 1,}
var disFreq = wave.frequency,disFreqChase = 0;
var disAmp = wave.amplitude,disAmpChase = 0;
var disY = wave.offset,disYChase = 0;
var disSp = wave.speed,disSpChase = 0;

const strokeColor = {width: 2,h:200,s:50,l:50 }
const backgroundColor = {  r:255,g:50,b:255,a:1 }

const waveFolder = gui.addFolder('wave')
waveFolder.add(wave,'speed',-1,1);  // peeks per second
waveFolder.add(wave,'offset',canvas.height);

waveFolder.add(wave,'amplitude',-100,100)
waveFolder.add(wave,'frequency',0.5,10); // frequency also defines wave length
                                         // Value is fraction of canvas width

const strokeFolder = gui.addFolder('stroke')
strokeFolder.add(strokeColor,'width',18)
strokeFolder.add(strokeColor,'h',255)
strokeFolder.add(strokeColor,'s',100)
strokeFolder.add(strokeColor,'l',100)

const backgroundFolder = gui.addFolder('background')
backgroundFolder.add(backgroundColor,'r',255)
backgroundFolder.add(backgroundColor,'g','b','a',1)



let increment = 0

function drawSineWave() { 

    // smooth out changes
    disFreq += disFreqChase = (disFreqChase += (wave.frequency - disFreq)*0.2)* 0.2;
    disAmp += disAmpChase = (disAmpChase += (wave.amplitude - disAmp) * 0.2) * 0.2;
    disY += disYChase = (disYChase += (wave.offset - disY) * 0.2) * 0.2;
    disSp += disSpChase = (disSpChase += (wave.speed - disSp) * 0.2) * 0.2;

    c.lineWidth = strokeColor.width;
   c.fillStyle = `rgba(${backgroundColor.r},${backgroundColor.a})` 
    c.clearRect(0,canvas.height)
    //increment++

    increment += (disSp / 60);
    c.beginPath()
    const h = canvas.height;
    for (let i=0;i<canvas.width;i++){
        const a = (disAmp / 200) * h;
        const x = ((i / canvas.width)  * disFreq) % 1  + increment;
        const p = x * Math.PI * 2;
        c.lineTo(i,disY + Math.sin(p)*a);
    }

    c.strokeStyle = `hsl(${strokeColor.h},${strokeColor.l}%)`
    c.stroke()
    c.lineTo(canvas.width,canvas.height)
    c.lineTo(0,canvas.height)
    c.fill()
}


function animate(){
    requestAnimationFrame(animate)
    drawSineWave()
}

animate()
*{
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

canvas{
    width: 100%;
    height: 100%;
}
<canvas></canvas>

<script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.7.7/dat.gui.min.js"></script>
<script src="index.js"></script>
</body>