Search code examples
canvas

How to rotate the gradient in the canvas, note: not the canvas itself


I have tried rotating the canvas includes transforming and translating, but they didn't work well

enter image description here

  grad = ctx.createRadialGradient(x1,y1,0,x1,y1,Math.pow((x1-x2)**2 + (y1-y2)**2,0.5))    //create radialGradient dynamically
  for (let i = 0; i < gradientList.length; i++) {        //Add colorStop dynamically
    grad.addColorStop(gradientList[i].point / 100, gradientList[i].color)
  }
  ctx.fillStyle = grad;
  ctx.rect(0, 0, w, h)          //draw a rect
  ctx.translate(w/2, h/2);           //and then... I dont know how to rotate
  ctx.rotate(Math.atan2(-x2+x1,y2-y1)/Math.PI);
  ctx.fill()
  ctx.rotate(-Math.atan2(-x2+x1,y2-y1)/Math.PI);
  ctx.translate(-w/2, -h/2);

Solution

  • From your comment I want to dynamically change the shape of the radial gradient below is an example of how you can do that...

    ...just a bit of fun with the position of the circles

    let canvas = document.getElementById("canvas");
    let ctx = canvas.getContext("2d");
    
    gradientList = ["red","blue","cyan","green"]
    circle1 = {x:canvas.width / 2, y:canvas.height / 2, r:10}
    circle2 = {x:canvas.width / 2, y:canvas.height / 2, r:100}
    i = 0
    
    function draw() {
      i += 0.1
      dx= Math.sin(i)*35
      dy= Math.cos(i)*20
      grad = ctx.createRadialGradient(
        circle1.x - dx, circle1.y - dy, circle1.r, 
        circle2.x + dx, circle2.y + dy, circle2.r + Math.abs(dx)
      ) 
      for (let i = 0; i < gradientList.length; i++) { 
        grad.addColorStop(i / 5, gradientList[i])
      }
      ctx.fillStyle = grad;
      ctx.rect(0, 0, canvas.width, canvas.height)    
      ctx.fill()
    }
    
    setInterval(draw, 50);
    <canvas id="canvas">

    But your image looks like an elliptical gradient the createRadialGradient creates a radial gradient using the size and coordinates of two circles (there is no long and short radius).
    Maybe you want to look at http://fabricjs.com/ they show we can apply gradients to any shapes.


    I was looking at how they are doing it on fabricjs and they are using SVG image...
    that is simple enough here is an example

    function svgimage() {
      var image = `
        <svg xmlns="http://www.w3.org/2000/svg" width="220" height="200" version="1.1">
          <defs>
            <radialGradient id="grey_blue" cx="50%" cy="50%" r="50%" fx="20%" fy="40%">
                <stop offset="0%" style="stop-color:gray;stop-opacity:0"/>
                <stop offset="100%" style="stop-color:blue;stop-opacity:1"/>
            </radialGradient>
          </defs>
          <ellipse cx="110" cy="100" rx="110" ry="100" style="fill:url(#grey_blue)"/>
        </svg>`;
      return encodeURIComponent(image);
    }
    
    function drawImage() {
      ctx.clearRect(0, 0, canvas.width * 2, canvas.height)
      dx = Math.sin(i) * 30
      dy = Math.cos(i) * 20
      ctx.drawImage(img, 50, 0, 200 + dx, 100 + dy);
      i += 0.1
    }
    
    function startDrawing() {
      ctx.rotate(20 * Math.PI / 180)
      setInterval(drawImage, 20);
    }
    
    
    var canvas = document.querySelector('canvas');
    var ctx = canvas.getContext('2d');
    var i = 0
    
    var img = new Image();
    img.onload = startDrawing
    img.src = 'data:image/svg+xml;charset=utf-8,' + svgimage();
    <canvas id=canvas width=250 height=250></canvas>