使用 jQuery 创建模式并重复到画布中

问题描述 投票:0回答:1

我正在研究画布上 SVG 的图案放置算法,它将是一个 WordPress 插件。我可以将 SVG 放在画布上并计算宽度和高度值,但是,由于 SVG 有凹痕和突出,因此仅考虑边界框是错误的,因为此方法会在图案之间产生间隙。我尝试正确放置下一个图案,考虑到图案的实际边界及其占据的区域,但我也无法获得所需的结果。无论如何,固定值并不能解决问题,因为我的图案具有非常不同的凹痕和突起。

这是我用我编写的代码得到的图像:

The image I obtained with these codes

这就是我想要达到的形象:

This is the image I am trying to achieve:

我已经被困在项目的这一点上一段时间了,我再也找不到解决方案了。我哪里错了?我认为我需要一个完全不同的视角,因为我无法通过面积计算、宽度和高度计算得到我想要的结果。谁能帮我解决这个问题吗?

这是我的代码:

jQuery(document).ready(function($) {
    const mainCanvas = $('#cmc-canvas')[0];
    const mainCtx = mainCanvas.getContext('2d');
    const svgElements = $('.cmc-svg-item');

    if (svgElements.length === 0) {
        return;
    }

    function loadAndDrawPattern() {
        const tempCanvas = document.createElement('canvas');
        const tempCtx = tempCanvas.getContext('2d');
        let loadedSVGs = 0;

        svgElements.each(function(index, imgElem) {
            const svgSrc = $(imgElem).attr('src');
            $.get(svgSrc, function(data) {
                const svgElement = $(data).find('svg')[0];
                if (svgElement) {
                    const svgData = new XMLSerializer().serializeToString(svgElement);
                    const svgBlob = new Blob([svgData], { type: 'image/svg+xml;charset=utf-8' });
                    const url = URL.createObjectURL(svgBlob);

                    const img = new Image();
                    img.onload = function() {
                        tempCtx.drawImage(img, 0, 0);
                        loadedSVGs++;
                        URL.revokeObjectURL(url);

                        if (loadedSVGs === svgElements.length) {
                            calculateRealDimensions(tempCanvas);
                        }
                    };

                    img.onerror = function() {
                        URL.revokeObjectURL(url);
                    };

                    img.src = url;
                }
            }).fail(function() {});
        });
    }

    function calculateRealDimensions(tempCanvas) {
        const imageData = tempCanvas.getContext('2d').getImageData(0, 0, tempCanvas.width, tempCanvas.height);
        let minX = tempCanvas.width, minY = tempCanvas.height;
        let maxX = 0, maxY = 0;

        for (let y = 0; y < imageData.height; y++) {
            for (let x = 0; x < imageData.width; x++) {
                const alpha = imageData.data[(y * imageData.width + x) * 4 + 3];
                if (alpha > 0) {
                    minX = Math.min(minX, x);
                    minY = Math.min(minY, y);
                    maxX = Math.max(maxX, x);
                    maxY = Math.max(maxY, y);
                }
            }
        }

        const realWidth = maxX - minX;
        const realHeight = maxY - minY;

        const patternCanvas = document.createElement('canvas');
        patternCanvas.width = realWidth;
        patternCanvas.height = realHeight;
        const patternCtx = patternCanvas.getContext('2d');
        patternCtx.drawImage(tempCanvas, minX, minY, realWidth, realHeight, 0, 0, realWidth, realHeight);

        const patternArea = calculatePatternArea(patternCanvas);
        fillCanvasWithDynamicPlacement(patternCanvas, patternArea);
    }

    function calculatePatternArea(tempCanvas) {
        const ctx = tempCanvas.getContext('2d');
        const imageData = ctx.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
        let pixelCount = 0;

        for (let i = 0; i < imageData.data.length; i += 4) {
            const alpha = imageData.data[i + 3];
            if (alpha > 0) {
                pixelCount++;
            }
        }

        return pixelCount;
    }

    function fillCanvasWithDynamicPlacement(patternCanvas, patternArea) {
        const patternWidth = patternCanvas.width;
        const patternHeight = patternCanvas.height;
        const mainCanvasWidth = mainCanvas.width;
        const mainCanvasHeight = mainCanvas.height;
    
        let currentX = 0;
        let currentY = 0;
    
        while (currentY < mainCanvasHeight) {
            while (currentX < mainCanvasWidth) {
                mainCtx.drawImage(patternCanvas, currentX, currentY, patternWidth, patternHeight);
                currentX += patternWidth;
            }
            currentX = 0;
            currentY += patternHeight;
        }
    }

    loadAndDrawPattern();
});

我简化并在 JSFiddle 中运行的代码:

document.addEventListener('DOMContentLoaded', () => {
    const mainCanvas = document.getElementById('cmc-canvas');
    const mainCtx = mainCanvas.getContext('2d');
    const svgString = `
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800">
<g>
    
        <rect x="264.67" y="155.14" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -64.6533 310.9802)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="264.67" y="376.87" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -221.4386 375.9228)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="264.67" y="598.6" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -378.224 440.8654)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="379.39" y="44.48" transform="matrix(0.7071 -0.7071 0.7071 0.7071 47.1975 359.6844)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="379.39" y="266.21" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -109.5878 424.627)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="379.39" y="487.94" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -266.3731 489.5696)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="493.72" y="155.05" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2.501 472.9144)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="493.72" y="376.78" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -154.2844 537.857)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="493.72" y="598.51" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -311.0697 602.7996)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="149.91" y="44" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -19.6769 197.2807)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="149.91" y="265.73" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -176.4623 262.2233)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="149.91" y="487.46" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -333.2476 327.166)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="35.37" y="155.19" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -131.8461 148.8535)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="35.37" y="376.92" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -288.6314 213.7962)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="35.37" y="598.65" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -445.4168 278.7388)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="609.15" y="44.62" transform="matrix(0.7071 -0.7071 0.7071 0.7071 114.3931 522.1906)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="609.15" y="266.35" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -42.3922 587.1331)" fill="#231F20" width="156.78" height="156.78"/>
</g>
<g>
    
        <rect x="609.15" y="488.08" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -199.1776 652.0757)" fill="#231F20" width="156.78" height="156.78"/>
</g>
    </svg>`;

    function loadSVGData(svgString, callback) {
        const svgBlob = new Blob([svgString], { type: 'image/svg+xml' });
        const url = URL.createObjectURL(svgBlob);
        const img = new Image();
        img.onload = function() {
            callback(img);
            URL.revokeObjectURL(url);
        };
        img.onerror = function() {
            console.error('SVG yüklenemedi.');
            URL.revokeObjectURL(url);
        };
        img.src = url;
    }

    function drawSVGOnCanvasRepeated() {
        loadSVGData(svgString, (img) => {
            const patternWidth = 100; // Desired width of each SVG pattern
            const patternHeight = 100; // Desired height of each SVG pattern

            let currentX = 0;
            let currentY = 0;

            while (currentY < mainCanvas.height) {
                while (currentX < mainCanvas.width) {
                    mainCtx.drawImage(img, currentX, currentY, patternWidth, patternHeight);
                    currentX += patternWidth;
                }
                currentX = 0;
                currentY += patternHeight;
            }
        });
    }

    drawSVGOnCanvasRepeated();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<div>
<canvas id="cmc-canvas" width="1020" height="450" style="border: 1px solid black;"></canvas>
</div>

jquery svg canvas constants
1个回答
0
投票

您需要引入一个新概念

offset
并用它来缩小差距...

下面的代码显着减少了你的代码,使其在代码片段中看起来更好,但从技术上讲是一样的,你的

svgString
它只是更长的行,我只有一个函数而不是两个。

您现在可以使用

const offset = { X: 11, Y: 14 }
的值来获得所需的结果。

document.addEventListener('DOMContentLoaded', () => {
    const mainCanvas = document.getElementById('cmc-canvas');
    const mainCtx = mainCanvas.getContext('2d');
    const svgString = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800">
<g><rect x="264.67" y="155.14" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -64.6533 310.9802)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="264.67" y="376.87" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -221.4386 375.9228)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="264.67" y="598.6" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -378.224 440.8654)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="379.39" y="44.48" transform="matrix(0.7071 -0.7071 0.7071 0.7071 47.1975 359.6844)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="379.39" y="266.21" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -109.5878 424.627)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="379.39" y="487.94" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -266.3731 489.5696)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="493.72" y="155.05" transform="matrix(0.7071 -0.7071 0.7071 0.7071 2.501 472.9144)" fill="#231F20" width="156.78" height="156.78"/></g>
<g><rect x="493.72" y="376.78" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -154.2844 537.857)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="493.72" y="598.51" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -311.0697 602.7996)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="149.91" y="44" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -19.6769 197.2807)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="149.91" y="265.73" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -176.4623 262.2233)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="149.91" y="487.46" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -333.2476 327.166)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="35.37" y="155.19" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -131.8461 148.8535)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="35.37" y="376.92" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -288.6314 213.7962)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="35.37" y="598.65" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -445.4168 278.7388)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="609.15" y="44.62" transform="matrix(0.7071 -0.7071 0.7071 0.7071 114.3931 522.1906)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="609.15" y="266.35" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -42.3922 587.1331)" fill="#231F20" width="156.78" height="156.78"/></g><g><rect x="609.15" y="488.08" transform="matrix(0.7071 -0.7071 0.7071 0.7071 -199.1776 652.0757)" fill="#231F20" width="156.78" height="156.78"/></g>
</svg>`;


    function drawSVGOnCanvasRepeated() {
        const svgBlob = new Blob([svgString], { type: 'image/svg+xml' });
        const url = URL.createObjectURL(svgBlob);
        const img = new Image();
        img.onload = function() {
            const pattern = { W: 100, H: 100 }
            const offset = { X: 11, Y: 14 }
            let current = { X:20, Y: 20 }

            while (current.Y < 200) {
                while (current.X < 200) {
                    mainCtx.drawImage(img, current.X, current.Y, pattern.W, pattern.H);
                    current.X += pattern.W - offset.X;
                }
                current.X = 20;
                current.Y += pattern.H - offset.Y;
            }
        };
        img.src = url;
    }

    drawSVGOnCanvasRepeated();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
<div>
<canvas id="cmc-canvas" width="400" height="400" style="border: 1px solid black;"></canvas>
</div>

© www.soinside.com 2019 - 2024. All rights reserved.