Crop image like pie chart
Asked Answered
H

2

8

I want to crop image over another image like a pie-chart to create a loading animation. I was thinking of using raphaeljs, but couldn't find any information about image cropping in pie-chart style.

Here are the sample images:

Start state:

Start state

End state:

End state

What it should look like:

What it should look like

Hoarfrost answered 18/3, 2015 at 19:38 Comment(0)
C
8

Just draw a semi-transparent filled arc on top of the image (adjust alpha value to your pleasing):

var ctx = document.querySelector("canvas").getContext("2d"),
    img = new Image;

img.onload = draw;
img.src = "http://i.imgur.com/hQ5Pljv.png";

function draw(){

  var cx = 157, cy = 159, r = 150,
      pst = 0,
      ang = Math.PI * 2 * (pst/100),
      dlt = 2;
  
  // animate the following part
  (function loop() {
    ctx.drawImage(img, 0, 0);
  
    ctx.beginPath();
    ctx.moveTo(cx, cy);
    ctx.arc(cx, cy, r, 0, ang);
    ctx.fillStyle = "rgba(0,0,0,0.33)";  // adjust alpha here
    ctx.fill();

    pst += dlt;
    if (pst <= 0 || pst >= 100) dlt = -dlt;
    ang = Math.PI * 2 * (pst/100);

    requestAnimationFrame(loop)
  })()
}
<canvas width=320 height=320></canvas>

Method two - compositing

Use two steps to clip the same arc above to use images instead:

  • Draw arc, this will be the composite data
  • Change comp. mode to source-atop - next drawing replaces the drawn arc
  • Draw secondary image in
  • Change comp. mode to destination-atop - next drawing will fill all non-pixels
  • Draw main image in

Demo:

var ctx = document.querySelector("canvas").getContext("2d"),
    img1 = new Image, img2 = new Image, cnt=2;

img1.onload = img2.onload = loader;
img1.src = "http://i.imgur.com/hQ5Pljv.png";
img2.src = "http://i.imgur.com/k70j3qp.jpg";

function loader(){if (!--cnt) draw()};                      
function draw(){
  var cx = 157, cy = 159, r = 150,
      pst = 0, ang = Math.PI * 2 * (pst/100), dlt = 2;
  
  // animate the following part
  (function loop() {
    ctx.clearRect(0, 0, 320, 320);   // clear canvas, or set last comp mode to "copy"
    
    // first arc
    ctx.beginPath();
    ctx.moveTo(cx, cy);
    ctx.arc(cx, cy, r, 0, ang);
    ctx.fill();       // this will be comp. basis for the next steps

    // comp mode secondary image
    ctx.globalCompositeOperation = "source-atop";      // replaces filled arc
    ctx.drawImage(img2, 0, 0);

    // comp mode main image
    ctx.globalCompositeOperation = "destination-atop"; // fills all non-pixels
    ctx.drawImage(img1, 0, 0);

    pst += dlt; if (pst <= 0 || pst >= 100) dlt = -dlt; ang = Math.PI * 2 * (pst/100);
    ctx.globalCompositeOperation = "source-over";  // reset comp. mode
    requestAnimationFrame(loop)
  })()
}
<canvas width=320 height=320></canvas>
Catullus answered 18/3, 2015 at 20:6 Comment(3)
The OP wants to use a second image, per the text, it just so happens that the two are very similar in their example.Acth
@Acth This works for me. But it would be nice to see how two images would work.Hoarfrost
@Hoarfrost added demo with image clipping as wellCatullus
A
1

You'll want an algorithm along the lines of:

  1. Draw image A onto canvas 1
  2. Clear canvas 2
  3. Draw a partial circle on canvas 2, for the current state of the spinner, filled with white
  4. Blit image B onto canvas 2, using the multiplicative blending mode
  5. Blit canvas 2 onto canvas 1, using standard (replace) blending

Canvas 2 should contain the second image, masked by the section you want to use. Overlaying that onto canvas 1, provided you handle transparency properly, should give the effect you want.

You can also use two SVG circles with image backgrounds and do this trivially, assuming your target browsers support SVG.

Acth answered 18/3, 2015 at 19:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.