Samuel Wittlinger
Samuel Wittlinger

Reputation: 15

How can I make my sine wave in javascript work properly?

I am trying to make a sine wave on a canvas that would be filled with a certain color. I was successful with creating the wave animation. However, i can't seem to get the fill right. You can see the way my code works in the snippet below

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', 0, canvas.height)
waveFolder.add(wave, 'length', -0.01, 0.01)
waveFolder.add(wave, 'amplitude', -300, 300)
waveFolder.add(wave, 'frequency', -0.01, 1)

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

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

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

let increment = 0

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

  c.moveTo(0, canvas.height / 2)
  c.lineTo(0, canvas.height / 2)
  c.lineTo(0, canvas.height)
  c.lineTo(canvas.width, 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>

The way I want the wave to look is this:

Proper filled sine wave

Can someone please help me achieve the desired result? Thanks!

Upvotes: 0

Views: 1273

Answers (1)

Blindman67
Blindman67

Reputation: 54026

You just need to draw the bottom half in the correct order

The result is

function drawSineWave() {
  c.fillStyle = `rgba(${backgroundColor.r},${backgroundColor.g},${backgroundColor.b},${backgroundColor.a})`
  c.clearRect(0, 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()
}

Example

I played about with it a bit.

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',0,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',0,18)
strokeFolder.add(strokeColor,'h',0,255)
strokeFolder.add(strokeColor,'s',0,100)
strokeFolder.add(strokeColor,'l',0,100)

const backgroundFolder = gui.addFolder('background')
backgroundFolder.add(backgroundColor,'r',0,255)
backgroundFolder.add(backgroundColor,'g',0,255)
backgroundFolder.add(backgroundColor,'b',0,255)
backgroundFolder.add(backgroundColor,'a',0,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.g},${backgroundColor.b},${backgroundColor.a})` 
    c.clearRect(0,0,canvas.width,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.s}%,${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>

Upvotes: 1

Related Questions