canvas如何从图片中抠出来一块图片保存下来?

已知不规则图形的坐标,如何从图形中抠出来这块图,类似于ps中的抠图(将抠出来的图放到新创建的画布,然后保存)?

阅读 4.8k
4 个回答
<script type="text/javascript">
    const imgLink = './avatar.png';
    const points = [
        { x: 0, y: 0 },
        { x: 0.5, y: 0 },
        { x: 0.5, y: 0.5 },
    ];
    
    
    function getImg(src) {
        return new Promise(resolve => {
            const img = new Image();
            img.addEventListener('load', () => resolve(img));
            img.addEventListener('error',() => resolve(null));
            img.src = src;
        });
    }
    function getRect(points) {
        const x = points.map(it => it.x);
        const y = points.map(it => it.y);
        const minX = Math.min(...x);
        const maxX = Math.max(...x);
        const minY = Math.min(...y);
        const maxY = Math.max(...y);
        console.log(x, y);
        return {
            x: minX, y: minY,
            width: maxX - minX,
            height: maxY - minY,
        };
    }
    function createCanvas({ width, height }) {
        const cvs = document.createElement('canvas');
        const ctx = cvs.getContext('2d');
        cvs.width = width;
        cvs.height = height;
        cvs.style.cssText = `
         height: ${height}px;
         width: ${width}px;
         `;
        return {
            cvs, ctx
        }
    }
    function outputImage(imageData, { width, height }) {
        const { cvs, ctx } = createCanvas({ width, height })
        ctx.putImageData(imageData, 0, 0)
        const src = cvs.toDataURL('image/png')
        return getImg(src)
    }
    async function clip({ imgLink, points }) {
        const img = await getImg(imgLink);
        console.log(img, img.width, img.height);
        if (!img || !img.height || points.length < 3) return;
        const { cvs, ctx } = createCanvas(img)
        const { width, height } = img;
        points = points.map(it => ({ x: it.x * width, y: it.y * height }));
        ctx.beginPath();
        const [point] = points;
        console.log(point, points);
        ctx.moveTo(point.x, point.y);
        points.forEach(point => {
            ctx.lineTo(point.x, point.y);
        });
        ctx.closePath();
        ctx.clip();
        ctx.drawImage(img, 0, 0);
        const rect = getRect(points);
        console.log(rect);
        const data = ctx.getImageData(rect.x, rect.y, rect.width, rect.height)
        const image = await outputImage(data, rect)
        document.body.appendChild(cvs);
        document.body.appendChild(image);
    }
    clip({
        imgLink,
        points
    });
</script>

image.png

下班了,先这样吧,溜了溜了

凸多边形还好,如果不是凸多边形, 点在多边形内这个算法就够折腾人的,看都看不懂。如果这个算法有了,就是扣像素的问题了。

难点是个不规则图形
如果是区域在颜色上有明显差异的,可以通过 imageData 逐一颜色筛选给抠出来。
或是区域边界有明显的差异区别应该也可以。
否则的话,你给 PS 它也不能自动抠出来。

绘图前将 CanvasRenderingContext2D.globalCompositeOperation设为 "source-in",这样画出来的是两个图形交叠的地方,也就达到了用一个形状去剪切另一个形状的效果。
然后根据剪切出来的图形裁剪出合适的盒子。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题