#javascript #html #canvas
#javascript #HTML #холст
Вопрос:
Например, скажем, у меня есть следующий путь.
<canvas id="main" width="500" height="250"></canvas>
var canvas = document.getElementById("main");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(20,20);
ctx.lineTo(100,20);
ctx.arcTo(150,20,150,70,50);
ctx.lineTo(150,120);
ctx.lineWidth = 3;
ctx.stroke();
Можно ли рисовать изображения по дуге линии? Если да, то как?
Ответ №1:
Нарежьте изображение для рисования на кривых.
Да, это возможно, хотя в идеале это было бы задачей для WebGL. Следующим лучшим решением является рендеринг строки сканирования, но это слишком большая нагрузка на процессор для управления плохим Javascript.
Следующий Лучшие Я имею в виду «вроде как нормально». опция — это небольшая нарезка изображения.
Вы просто рисуете изображение тонкими ломтиками вокруг дуги. 2D-рендеринг не идеален и пытается нарисовать половину пикселей как можно лучше. Результатом является некоторый шум вдоль края каждого фрагмента, где вы можете видеть. Чтобы преодолеть это, я рисую каждый фрагмент немного шире, чтобы скрыть любые отверстия.
Если вам нужно высокое качество, отрисовка всего этого в двойном размере на внеэкранном холсте, а затем уменьшение масштаба до экранного холста (не забудьте о сглаживании) заставит большинство думать, что оно было нарисовано именно так.
Поскольку внутренний и внешний края дуги имеют разные окружности, часть изображения должна быть сжата или растянута. В демонстрации я сохраняю внутренний край изображения правильной ширины и растягиваю внешний край. Его легко изменить, но убедитесь, что вы используете внешний край, чтобы определить, сколько срезов нужно нарисовать.
ПРЕДУПРЕЖДЕНИЕ указанный радиус предназначен для внутреннего края. Проверяется, чтобы цикл for не становился слишком длинным и не блокировал страницу. Возможно, вы захотите ограничить радиус, чтобы внутренняя окружность совпадала с шириной изображения. radius = radius < img.width / (Math.PI * 2) ? img.width / (Math.PI * 2) : radius;
Его легко адаптировать к линиям и кривым. Все, что вам нужно, это касательная или нормаль кривой (должна быть единичным вектором, т.Е. Длиной 1) Используйте этот вектор для задания преобразования ctx.setTransform(nx,ny,tx,ty,px,py)
. Первые два значения указывают от нижней части изображения к верхней, следующие два числа идут по касательной слева направо. Последние две точки на кривой для рисования среза.
// creates a blank image with 2d context
var createImage=function(w,h){var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i;}
// create a canvas and add to dom
var can = createImage(512,512);
document.body.appendChild(can);
var ctx = can.ctx;
// create a image (canvas) to draw on the arc.
const textToDisplay = "<<Image on arc>>"
ctx.font = "64px arial";
var w = ctx.measureText(textToDisplay).width 8;
var text = createImage(w 64,84);
text.ctx.fillStyle = "#F90";
text.ctx.strokeStyle = "black";
text.ctx.lineWidth = 16;
text.ctx.fillRect(0,0,text.width,text.height);
text.ctx.strokeRect(0,0,text.width,text.height);
text.ctx.font = "64px arial";
text.ctx.fillStyle = "#0F0";
text.ctx.strokeStyle = "Black";
text.ctx.lineWidth = 4;
text.ctx.strokeText(textToDisplay,38,58);
text.ctx.fillText(textToDisplay,38,58);
// draws image on arc
// img image to render
// x,y center of arc
// radius the inner edge (bottom of image) radius
// fromAng The angle to start drawing the image in radians
// toAng (optional if not given image width will be used to get toAng)
// returns undefined
function drawArcImage(img,x,y,radius,fromAng,toAng){
// WARNING if you let the radius get to small the ratio between the inner and out circumference
// gets very large. This will result in the image being stretched over a quintabazzilon pixels.
// so must vet the radius or you will block the page and upset the browser gods.
radius = Math.abs(radius); // only positive
radius = radius < img.height / 8 ? img.height / 8 : radius;
var outRad = radius img.height;
var cir = Math.PI * 2 * radius; // get inner circumference
if(toAng === undefined){
var toAng = (img.width / cir) * Math.PI * 2 ; // get the angle the image will cover
}
var cirOut = toAng * outRad; // get the out edge distance in pixels
var imgStep = img.width / cirOut; // the image step per slice
var imgX = 0; // track the image line to draw
var angStep = toAng / cirOut; // the angle steps
// For each pixel on the out edge draw a slice
for(var i = 0; i < toAng; i = angStep){
var dx = Math.cos(fromAng i);
var dy = Math.sin(fromAng i);
// set up the transform to draw a slice from the inner to outer edges
ctx.setTransform(dy,-dx,-dx,-dy,dx * radius x,dy * radius y);
// get and draw the slice. I stretch it a little (2pix) to cover imperfect rendering
ctx.drawImage(img,imgX,0,imgStep,img.height,-1,-img.height,2,img.height);
// move to next slice
imgX = imgStep;
}
ctx.setTransform(1,0,0,1,0,0); // reset the transform
}
// animate the image to prove it is real.. LOL
var animTick = 0;
var animRate = 0.01;
var pos = 0;
// update function call via RAF
function update(){
animTick = animRate; // update tick
// random anim sin waves.
var rad = Math.sin(animTick) * (256-text.height - 20) 20;
pos = Math.sin(animTick*10) * 0.02;
pos = Math.sin(animTick/ 3) * 0.02;
pos = Math.sin(animTick/ 7) * 0.05;
// clear
ctx.clearRect(0,0,can.width,can.height)
// draw
drawArcImage(text,256,256,rad,pos)
// do again and again and again
requestAnimationFrame(update);
}
update();
Ответ №2:
Это ответ на аналогичный вопрос:
Вы могли бы в цикле рисования реализовать «алгоритм рисования линий», который точно не рисует линию, а рисует элемент в том месте, где будет находиться эта точка. Кроме того, замените алгоритм line здесь, чтобы вместо этого нарисовать дугу.
function line(x0, y0, x1, y1){
var dx = Math.abs(x1-x0);
var dy = Math.abs(y1-y0);
var sx = (x0 < x1) ? 1 : -1;
var sy = (y0 < y1) ? 1 : -1;
var err = dx-dy;
while(true){ // put draw loop here.
drawImage(image,x0,y0);//setPixel(x0,y0); // Do what you need to for this
if ((x0==x1) amp;amp; (y0==y1)) break;
var e2 = 2*err;
if (e2 >-dy){ err -= dy; x0 = sx; }
if (e2 < dx){ err = dx; y0 = sy; }
}
}
код взят из: алгоритм Брезенхема в Javascript
Я бы предложил использовать библиотеку типа p5.js чтобы сделать что-то подобное. http://p5js.org
Комментарии:
1. Спасибо за это. На самом деле я не собираюсь использовать какие-либо библиотеки, это то, что я хочу попробовать сделать без библиотеки или плагина. Делает его более интересным для меня.