我正在研究画布上 SVG 的图案放置算法,它将是一个 WordPress 插件。我可以将 SVG 放在画布上并计算宽度和高度值,但是,由于 SVG 有凹痕和突出,因此仅考虑边界框是错误的,因为此方法会在图案之间产生间隙。我尝试正确放置下一个图案,考虑到图案的实际边界及其占据的区域,但我也无法获得所需的结果。无论如何,固定值并不能解决问题,因为我的图案具有非常不同的凹痕和突起。
这是我用我编写的代码得到的图像:
这就是我想要达到的形象:
我已经被困在项目的这一点上一段时间了,我再也找不到解决方案了。我哪里错了?我认为我需要一个完全不同的视角,因为我无法通过面积计算、宽度和高度计算得到我想要的结果。谁能帮我解决这个问题吗?
这是我的代码:
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>
您需要引入一个新概念
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>