【问题标题】:How to merge 2 images (one with shape) and make it transparant in an Canvas如何合并 2 个图像(一个有形状)并使其在 Canvas 中透明
【发布时间】:2019-12-18 16:29:20
【问题描述】:

我有 2 张海盗的图像,一张带有形状,一张带有海盗本身。我想将这 2 张图片合并在一起,以创建一个带有透明边框的图像,以便在我的画布中使用。唯一的问题是我找不到真正管理它的方法。

图像本身自然根本不透明,所以我在 javascript 中使用复合选项,尝试使用“更轻”选项使其透明,但我找不到摆脱周围白色边框的方法图片。

这是我目前拥有的代码的一部分:

ctx.canvas.width = pirate_shape.width;
ctx.canvas.height = pirate_shape.height;

ctx.clearRect(0, 0, pirate_shape.width, pirate_shape.height);

// Draw the shape in reversed colors so i can draw the picture inside the shape.

ctx.drawImage(pirate_shape, 0, 0);
ctx.globalCompositeOperation='difference';
ctx.fillStyle='white';
ctx.fillRect(0, 0, pirate_shape.width, pirate_shape.height);

// Draw the pirate itself
ctx.globalCompositeOperation = 'lighter';
ctx.drawImage(pirate, 0, 0);

完整代码:https://jsfiddle.net/0kbj2Leg/

我试图得到的结果:

https://imgur.com/uL0Pf4T

我使用的两张图片:

(海盗) https://imgur.com/8R1qutU

(形状) https://imgur.com/qemOzU2


你们知道获得这个结果的方法吗?

谢谢!

【问题讨论】:

    标签: javascript html image-processing canvas


    【解决方案1】:

    您想使用合成,而不是混合。

    为此,您需要透明度。您的图片不包含任何内容。

    由于您的边框图像是黑白的,因此在现代浏览器中实际上有一种简单的方法可以将白色转换为透明像素:svg 过滤器。

    完成后,我们可以使用合成来实现我们的目标。

    // just the assets loader
    (async () => {
    const [face, border, back] = await Promise.all(
      [
        'https://i.imgur.com/8R1qutU.png',
        'https://imgur.com/qemOzU2.png',
        'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png'
      ].map(url => {
        const img = new Image();
        img.src = url;
        return new Promise(res => {
          img.onload = e =>  res(img);
        });
      })
    );
    
    
    const ctx = document.getElementById('canvas').getContext('2d');
    
    // generate the border transparency based on luminance
    // use the brightness to control how much red we keep
    ctx.filter = 'brightness(180%) url(#trans)';
    ctx.drawImage(border, 10, 10);
    ctx.filter = 'none';
    
    await wait(1000); // only for demo
    
    // draw the face only where we already have the border drawn
    ctx.globalCompositeOperation = 'source-in';
    ctx.drawImage(face, 10, 10); 
    
    await wait(1000); // only for demo
    
    // draw the background behind
    ctx.globalCompositeOperation = 'destination-over';
    ctx.drawImage(back, -100,0, 400,300);
    
    // reset
    ctx.globalCompositeOperation = 'source-over';
    
    })();
    
    // only for demo, so we can see the different steps
    function wait(ms) { return new Promise(res => setTimeout(res, ms)) }
    canvas {  
      background-color: ivory;
      border: 1px solid lightgray;
    }
    <canvas id="canvas" width="200" height="200"></canvas>
    <svg style="width:0px;height:0px;position:absolute;z-index:-1">
      <defs>
        <filter id="trans">
          <feColorMatrix type="luminanceToAlpha"/>
        </filter>
      </defs>
    </svg>

    现在,如果您能从一开始就正确准备好资产,显然会容易得多:

    // just the assets loader
    (async () => {
    const [face, border, back] = await Promise.all(
      [
        'https://i.imgur.com/8R1qutU.png',
        'https://i.stack.imgur.com/778ZM.png', // already wth transparency
        'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png'
      ].map(url => {
        const img = new Image();
        img.src = url;
        return new Promise(res => {
          img.onload = e =>  res(img);
        });
      })
    );
    
    
    const ctx = document.getElementById('canvas').getContext('2d');
    
    ctx.drawImage(border, 10, 10);
    
    // draw the face only where we already have the border drawn
    ctx.globalCompositeOperation = 'source-in';
    ctx.drawImage(face, 10, 10); 
    
    // draw the background behind
    ctx.globalCompositeOperation = 'destination-over';
    ctx.drawImage(back, -100,0, 400,300);
    
    // reset
    ctx.globalCompositeOperation = 'source-over';
    
    })();
    canvas {  
      background-color: ivory;
      border: 1px solid lightgray;
    }
    &lt;canvas id="canvas" width="200" height="200"&gt;&lt;/canvas&gt;

    但是您甚至可以通过在精心准备的海盗图像上画阴影来完全摆脱边框图像:

    // just the assets loader
    (async () => {
    const [face,back] = await Promise.all(
      [
        'https://i.stack.imgur.com/1hzcD.png',
        'https://upload.wikimedia.org/wikipedia/commons/4/47/PNG_transparency_demonstration_1.png'
      ].map(url => {
        const img = new Image();
        img.src = url;
        return new Promise(res => {
          img.onload = e =>  res(img);
        });
      })
    );
    
    
    const ctx = document.getElementById('canvas').getContext('2d');
    
    // draw the background
    ctx.drawImage(back, -100,0, 400,300);
    
    // draw the face with a shadow
    ctx.shadowBlur = 15;
    ctx.shadowColor = "red";
    
    // little hack to make shadow more opaque
    
    // first move your shadow away from what you wish to draw
    ctx.shadowOffsetX = face.width + 10; // + 10 because we'll draw the last one at x:10
    ctx.shadowOffsetY = face.height + 10;
    
    // now draw your shape outside of the visible area
    ctx.drawImage(face, -face.width, -face.height);
    await wait(1000); // just for demo
    ctx.drawImage(face, -face.width, -face.height);
    // we now have to shadows overlapping but not our shape
    await wait(1000); // just for demo
    // reset the shadowOffset
    ctx.shadowOffsetX = ctx.shadowOffsetY = 0;
    // adn draw the last one
    ctx.drawImage(face, 10, 10);
    
    ctx.shadowBlur = 0;
    ctx.shadowColor = 'transparent';
    })();
    
    // only for demo, so we can see the different steps
    function wait(ms) { return new Promise(res => setTimeout(res, ms)) }
    canvas {  
      background-color: ivory;
      border: 1px solid lightgray;
    }
    &lt;canvas id="canvas" width="200" height="200"&gt;&lt;/canvas&gt;

    【讨论】:

    • 谢谢,这正是我要找的地方:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    • 2012-03-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多