#javascript #svg #canvas
#javascript #svg #холст
Вопрос:
Я создаю инструмент рисования, и одной из функций является отображение обрезанного изображения нарисованного пути.
Путь, который я нарисовал (изображение)
Например, на рисунке выше контур белого цвета указывает на то, что я нарисовал, точно так же, как инструмент рисования.
И вот обрезанное изображение пути. Если вы посмотрите на картинку, вы можете увидеть, что она обрезает изображение так, как будто путь закрыт, и поэтому она обрезает «область» изображения, а не путь.
и вот код
function crop({ image, points }) {
return Observable.create(observer => {
const { width, height } = getImageSize(image);
const canvas = document.createElement('canvas') as HTMLCanvasElement;
const context = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
context.beginPath();
points.forEach(([x, y], idx) => {
if (idx === 0) {
context.moveTo(x, y);
} else {
context.lineTo(x, y);
}
});
context.clip();
context.drawImage(image);
...etc
}
crop
Функция получает points
, который состоит из [координаты x, координаты y][ ] нарисованного пути.
Есть ли способ показать изображению только тот путь, который я нарисовал?
Ответ №1:
Тогда это больше похоже на то, что обычно называют маской, но обратите внимание, что как для текущего клипа, так и для маски, которую вы хотите получить, лучше всего использовать компоновку.
Контекст холста имеет различные параметры компоновки, позволяющие создавать сложные композиции на основе альфа-значения пикселей.
const ctx = canvas.getContext('2d');
const pathes = [[]];
let down = false;
let dirty = false;
const bg = new Image();
bg.onload = begin;
bg.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Serene_Sunset_(26908986301).jpg/320px-Serene_Sunset_(26908986301).jpg';
function begin() {
canvas.width = this.width;
canvas.height = this.height;
ctx.lineWidth = 10;
addEventListener('mousemove', onmousemove);
addEventListener('mousedown', onmousedown);
addEventListener('mouseup', onmouseup);
anim();
ctx.fillText("Use your mouse to draw a path", 20,50)
}
function anim() {
requestAnimationFrame(anim);
if(dirty) draw();
dirty = false;
}
function draw() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.drawImage(bg, 0, 0);
ctx.beginPath();
pathes.forEach(path => {
if(!path.length) return;
ctx.moveTo(path[0].x, path[0].y);
path.forEach(pt => {
ctx.lineTo(pt.x, pt.y);
});
});
// old drawings will remain on where new drawings will be
ctx.globalCompositeOperation = 'destination-in';
ctx.stroke();
// reset
ctx.globalCompositeOperation = 'source-over';
}
function onmousemove(evt) {
if(!down) return;
const rect = canvas.getBoundingClientRect();
pathes[pathes.length - 1].push({
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
});
dirty = true;
}
function onmousedown(evt) {
down = true;
}
function onmouseup(evt) {
down = false;
pathes.push([]);
}
canvas {border: 1px solid}
<canvas id="canvas"></canvas>
Не стесняйтесь просматривать все варианты компоновки, в разных случаях потребуются разные параметры, например, если вам нужно нарисовать несколько контуров, вы можете предпочесть сначала отобразить свои контуры, а затем сохранить изображение только там, где вы его уже нарисовали, используя source-atop
опцию:
const ctx = canvas.getContext('2d');
const pathes = [[]];
pathes[0].lineWidth = (Math.random() * 20) 0.2;
let down = false;
let dirty = false;
const bg = new Image();
bg.onload = begin;
bg.src = 'https://upload.wikimedia.org/wikipedia/commons/thumb/e/ec/Serene_Sunset_(26908986301).jpg/320px-Serene_Sunset_(26908986301).jpg';
function begin() {
canvas.width = this.width;
canvas.height = this.height;
addEventListener('mousemove', onmousemove);
addEventListener('mousedown', onmousedown);
addEventListener('mouseup', onmouseup);
anim();
ctx.fillText("Use your mouse to draw a path", 20,50)
}
function anim() {
requestAnimationFrame(anim);
if(dirty) draw();
dirty = false;
}
function draw() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
pathes.forEach(path => {
if(!path.length) return;
ctx.beginPath();
ctx.lineWidth = path.lineWidth;
ctx.moveTo(path[0].x, path[0].y);
path.forEach(pt => {
ctx.lineTo(pt.x, pt.y);
});
ctx.stroke();
});
// new drawings will appear on where old drawings were
ctx.globalCompositeOperation = 'source-atop';
ctx.drawImage(bg, 0, 0);
// reset
ctx.globalCompositeOperation = 'source-over';
}
function onmousemove(evt) {
if(!down) return;
const rect = canvas.getBoundingClientRect();
pathes[pathes.length - 1].push({
x: evt.clientX - rect.left,
y: evt.clientY - rect.top
});
dirty = true;
}
function onmousedown(evt) {
down = true;
}
function onmouseup(evt) {
down = false;
const path = [];
path.lineWidth = (Math.random() * 18) 2;
pathes.push(path);
}
canvas {border: 1px solid}
<canvas id="canvas"></canvas>
И также помните, что у вас вполне могут быть холсты, которые вы не будете добавлять в документ, которые вы можете использовать в качестве слоев для создания действительно сложных композиций. ( drawImage()
принимает <canvas> в качестве источника).
Комментарии:
1. Ваш ответ мне очень помог. Я на один шаг далек от того, что я хочу реализовать. Последний вопрос, есть ли способ создать несколько рисунков «назначения»? Я нарисовал 3 отдельных рисунка, которые имеют разную ширину линии (которая не связана с ними для выполнения 3 отдельных context.beginPath), но ничего не остается, потому что ну, ‘destination-in’ сохраняет содержимое холста, где новая форма и существующее содержимое перекрываются, и поскольку я каждый раз генерирую «3»новых» и «разделенных» фигуры, они рисуются после каждого обработанного содержимого.
2. Если все они имеют одинаковый фон, то сделайте это наоборот, используя
source-atop
: нарисуйте свои контуры поверх исходного кода, а затем нарисуйте изображение только там, где вы уже нарисовали, используя source-atop . Я отредактирую свой ответ.3. ты мой герой, все заработало после выполнения того, что ты мне сказал 🙂 большое спасибо
4. могу ли я задать вам быстрый, связанный с этим вопрос о Canvas и SVG? Каковы возможные способы рисования объекта на SVG, который получен по типу изображения. Например, я получил изображение простого черного объекта в форме пончика на белом фоне. Я рисую это изображение на холсте и получаю все координаты пикселей, цвет которых равен #000000. И с этими координатами я рисую ломаную линию в SVG. есть ли какие-либо лучшие способы добиться этого? Я бы, конечно, хотел услышать ваше мнение.
5. Я думаю, будет лучше создать свой собственный вопрос 🙂 Я дам вам знать, когда сделаю одно, я вернусь через минуту