我想在画布中将几条腿显示为矩形。 基于一个将我的腿的英里数组合在一起的阵列,我已经使算法成比例地在给定的画布上代表它们。
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var width = c.width;
var somme = 0;
var prevValue = 0;
var recapProp = [];
function drawArrow(fromx, fromy, tox, toy){
//variables to be used when creating the arrow
var headlen = 5;
var angle = Math.atan2(toy-fromy,tox-fromx);
//starting path of the arrow from the start square to the end square and drawing the stroke
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
ctx.stroke();
//starting a new path from the head of the arrow to one of the sides of the point
ctx.beginPath();
ctx.moveTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//path from the side point of the arrow, to the other side point
ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
//path from the side point back to the tip of the arrow, and then again to the opposite side point
ctx.lineTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
//draws the paths created above
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
function drawCircle(centerXFrom, centerYFrom){
var radius = 3;
ctx.beginPath();
ctx.arc(centerXFrom, centerYFrom, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'green';
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#003300';
ctx.stroke();
ctx.beginPath();
}
function sumTab(tabTT){
for (var i = 0; i < tabTT.length; i++){
somme += tabTT[i];
}
return somme;
}
function findProportion(tabTT){
var tailleMax = tabTT.length;
sumTab(tabTT);
for(var i = 0; i < tabTT.length; i++){
var percentLeg = (tabTT[i]/somme)*100;
var tailleLeg = ((width- 20)*percentLeg)/100 ;
recapProp.push(tailleLeg);
}
for(var i = 0; i <= recapProp.length; ++i){
console.log(prevValue);
drawCircle(prevValue +5, 5);
drawArrow(prevValue + 7, 5, prevValue+recapProp[i],5);
prevValue += recapProp[i];
}
}
var tabTT = [0,5,1,8,2];
findProportion(tabTT);
&#13;
<canvas id="myCanvas" height="200" width="500"></canvas>
&#13;
然后,我想以矩形形式显示,以形成一个循环(下面不是矩形,但它可以帮助你理解):
我试图操纵quadracticCurveTo()
,但这并不是真正的结论......
var c=document.getElementById("myCanvas");
var ctx=c.getContext("2d");
function drawArrow(fromx, fromy, tox, toy, radius){
//variables to be used when creating the arrow
var headlen = 5;
var r = fromx + tox;
var b = fromy + toy;
var angle = Math.atan2(r,b);
//starting path of the arrow from the start square to the end square and drawing the stroke
ctx.beginPath();
ctx.moveTo(fromx+radius, fromy);
ctx.lineTo(r-radius, fromy);
ctx.quadraticCurveTo(r, fromy, r, fromy+radius);
ctx.lineWidth = "2";
ctx.strokeStyle = '#ff0000';
ctx.stroke();
//starting a new path from the head of the arrow to one of the sides of the point
ctx.beginPath();
ctx.moveTo(r, b);
ctx.lineTo(r-headlen*Math.cos(angle-Math.PI/7),b-headlen*Math.sin(angle-Math.PI/7));
//path from the side point of the arrow, to the other side point
ctx.lineTo(r-headlen*Math.cos(angle+Math.PI/7),b-headlen*Math.sin(angle+Math.PI/7));
//path from the side point back to the tip of the arrow, and then again to the opposite side point
ctx.lineTo(r, b);
ctx.lineTo(r-headlen*Math.cos(angle-Math.PI/7),b-headlen*Math.sin(angle-Math.PI/7));
//draws the paths created above
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
drawArrow(50,5, 80,25, 25);
&#13;
<canvas id="myCanvas" height="2000" width="2000"></canvas>
&#13;
最后,当我知道如何曲线并保持其长度时,我已经创建了我需要的片段。。我计算了画布表面的周长,以便重新计算我腿部的比例。
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
var width = c.width;
var height = c.height;
var perimetre = (width*2 + height*2);
var up = 0;
var right = 0;
var left = 0;
var bot = 0;
var somme = 0;
var prevValue = 0;
var recapProp = [];
/**********************************/
/*****<<Straight>> Arrows*********/
/********************************/
function drawArrow(fromx, fromy, tox, toy){
var headlen = 5;
var angle = Math.atan2(toy-fromy,tox-fromx);
ctx.beginPath();
ctx.moveTo(fromx, fromy);
ctx.lineTo(tox, toy);
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
ctx.stroke();
ctx.beginPath();
ctx.moveTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
ctx.lineTo(tox-headlen*Math.cos(angle+Math.PI/7),toy-headlen*Math.sin(angle+Math.PI/7));
ctx.lineTo(tox, toy);
ctx.lineTo(tox-headlen*Math.cos(angle-Math.PI/7),toy-headlen*Math.sin(angle-Math.PI/7));
ctx.strokeStyle = "blue";
ctx.lineWidth = 2;
ctx.stroke();
ctx.fillStyle = "blue";
ctx.fill();
}
/**********************************/
/************Points***************/
/********************************/
function drawCircle(centerXFrom, centerYFrom){
var radius = 3;
ctx.beginPath();
ctx.arc(centerXFrom, centerYFrom, radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'green';
ctx.fill();
ctx.lineWidth = 1;
ctx.strokeStyle = '#003300';
ctx.stroke();
ctx.beginPath();
}
function sumTab(tabTT){
for (var i = 0; i < tabTT.length; i++){
somme += tabTT[i];
}
return somme;
}
/***************************************************/
/************Get length for each leg***************/
/*************************************************/
function findProportion(tabTT){
var tailleMax = tabTT.length;
sumTab(tabTT);
for(var i = 0; i < tabTT.length; i++){
var percentLeg = (tabTT[i]/somme)*100;
var tailleLeg = ((perimetre - 20)*percentLeg)/100 ;
recapProp.push(tailleLeg);
}
/* For each leg I draw the circle and the arrow, due to the length calculated previously. If the length > the width of the canva, the arrow has to be curved */
for(var i = 0; i <= recapProp.length; ++i){
if(prevValue > width && top == 1){
drawCircle(prevValue +5, 5);
drawArrowBot(prevValue + 7, 5, prevValue+recapProp[i],5);
right = 1;
top = 0;
}
else if(prevValue > height && right == 1){
drawCircle(prevValue +5, 5);
drawArrowLeft(prevValue + 7, 5, prevValue+recapProp[i],5);
bot = 1;
right = 0;
}
else if (prevValue > width && bot == 1){
drawCircle(prevValue +5, 5);
drawArrowTop(prevValue + 7, 5, prevValue+recapProp[i],5);
bot = 0;
left = 0;
}
else {
drawCircle(prevValue +5, 5);
drawArrow(prevValue + 7, 5, prevValue+recapProp[i],5);
}
prevValue += recapProp[i];
}
}
var tabTT = [0,5,1,8,2];
findProportion(tabTT);
&#13;
<canvas id="myCanvas" height="200" width="500" style="border:1px solid #000000;"></canvas>
&#13;
我已经对我的所有代码进行了评论,以帮助您了解逻辑和我想要的内容。
那么,是否可以以通用方式对线进行曲线化?
答案 0 :(得分:2)
我可能会这样做:
现在,你可以将线条生成几乎任何形状和形状的你想要的。
提示:您当然可以通过第一次直接映射来跳过一个形状 提示2:可以在标准化坐标中定义形状,这样可以更容易地进行平移和缩放。
这里我们定义一个圆角正方形和圆形,然后将线条映射到任一个,我们可以在形状之间进行变换以找到我们喜欢的组合并使用它(注意:因为此示例中的方形在“右上角”开始角落而不是圆圈的位置是0°,也会有一个小的旋转,这可以作为练习单独处理。
圆角正方形可能是一个兔子(对于更“紧”的圆角正方形,你可以使用 cubic Bezier而不是像这里的二次方)。关键点在于形状可以独立于线条本身来定义。这可能是矫枉过正,但它不是那么复杂,而且它是多功能的,即。通用的。
有关向线条添加箭头的一种方法,请参阅this answer。
var ctx = document.querySelector("canvas").getContext("2d"),
resolution = 2000,
raster = new Uint8Array(resolution), // line raster array
shape = new Float32Array(resolution * 2), // target shape array (x2 for x/y)
shape2 = new Float32Array(resolution * 2),// target shape array 2
lines = [100, 70, 180, 35], // lines, lengths only
tLen = 0, // total length of lines + gaps
gap = 20, // gap in pixels
gapNorm, // normalized gap value for mapping
p = 0, // position in lines array
radius = 100, // target circle radius
angleStep = Math.PI * 2 / resolution, // angle step to reach circle / res.
cx = 150, cy = 150, // circle center
interpolation = 0.5, // t for interpolation
i;
// get total length of lines + gaps so we can normalize
for(i = 0; i < lines.length; i++) tLen += lines[i];
tLen += (lines.length - 2) * gap;
gapNorm = gap / tLen * 0.5;
// convert line and gap ranges to "on" in the lines array
for(i = 0; i < lines.length; i++) {
var sx = p, // start position in lines array
ex = p + ((lines[i] / tLen) * resolution)|0; // end position in lines array (int)
// fill array
while(sx <= ex) raster[sx++] = 1;
// update arrqay pointer incl. gap
p = ex + ((gapNorm * resolution)|0);
}
// Create a circle target shape split into same amount of segments as lines array:
p = 0; // reset pointer for shape array
for(var angle = 0; angle < Math.PI*2; angle += angleStep) {
shape[p++] = cx + radius * Math.cos(angle);
shape[p++] = cy + radius * Math.sin(angle);
}
// create a rounded rectangle
p = i = 0;
var corners = [
{x1: 250, y1: 150, cx: 250, cy: 250, x2: 150, y2: 250}, // bottom-right
{x1: 150, y1: 250, cx: 50, cy: 250, x2: 50, y2: 150}, // bottom-left
{x1: 50, y1: 150, cx: 50, cy: 50, x2: 150, y2: 50}, // upper-left
{x1: 150, y1: 50, cx: 250, cy: 50, x2: 250, y2: 150} // upper-right
],
c, cres = resolution * 0.25;
while(c = corners[i++]) {
for(var t = 0; t < cres; t++) {
var pos = getQuadraticPoint(c.x1, c.y1, c.cx, c.cy, c.x2, c.y2, t / cres);
shape2[p++] = pos.x;
shape2[p++] = pos.y;
}
}
// now we can map the lines array onto our shape depending on the values
// interpolation. Make it a reusable function so we can regulate the "morph"
function map(raster, shape, shape2, t) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
ctx.beginPath();
for(var i = 0, x, y, x1, y1, x2, y2, prev = 0; i < resolution; i++) {
x1 = shape[i*2];
y1 = shape[i*2 + 1];
x2 = shape2[i*2];
y2 = shape2[i*2 + 1];
x = x1 + (x2 - x1) * t;
y = y1 + (y2 - y1) * t;
// do we have a change?
if (prev !== raster[i]) {
if (raster[i]) { // it's on, was off. create sub-path
ctx.moveTo(x, y);
}
else { // it's off, was on, render and reset path
ctx.stroke();
ctx.beginPath();
// create "arrow"
ctx.moveTo(x + 3, y);
ctx.arc(x, y, 3, 0, 6.28);
ctx.fill();
ctx.beginPath();
}
}
// add segment if on
else if (raster[i]) {
ctx.lineTo(x, y);
}
prev = raster[i];
}
}
ctx.fillStyle = "red";
map(raster, shape, shape2, interpolation);
document.querySelector("input").onchange = function() {
map(raster, shape, shape2, +this.value / 100);
};
function getQuadraticPoint(z0x, z0y, cx, cy, z1x, z1y, t) {
var t1 = (1 - t), // (1 - t)
t12 = t1 * t1, // (1 - t) ^ 2
t2 = t * t, // t ^ 2
t21tt = 2 * t1 * t; // 2(1-t)t
return {
x: t12 * z0x + t21tt * cx + t2 * z1x,
y: t12 * z0y + t21tt * cy + t2 * z1y
}
}
<script src="https://cdn.rawgit.com/epistemex/slider-feedback/master/sliderfeedback.min.js"></script>
<label>Interpolation: <input type="range" min=0 max=400 value=50></label><br>
<canvas width=400 height=400></canvas>
答案 1 :(得分:2)
计算使二次贝塞尔曲线成为指定长度的中间控制点。
<强>假设:强>
p0
,p2
:QCurves的起点和终点。length
:二次贝塞尔曲线的理想弧长。 您可以计算使QCurve的总弧长等于length
的控制点:
p1
)。这是一个可能的控制点。垂直角度是从步骤#2减去90度的计算角度。calculatedLength
)。calculatedLength
等于所需的length
,则您拥有正确的中间控制点。以下是示例代码和演示:
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
var BB=canvas.getBoundingClientRect();
offsetX=BB.left;
offsetY=BB.top;
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
var $length=$('#length');
var PI2=Math.PI*2;
var radius=5+1; // 5==fill, 1=added stroke
var p0={x:50,y:100,color:'red'};
var p2={x:175,y:150,color:'gold'};
var p1={x:0,y:0,color:'green'};
var midpoint={x:0,y:0,color:'purple'};
var perpendicularPoint={x:0,y:0,color:'cyan'};
//var points=[p0,p1,p2];
//var draggingPoint=-1;
setQLength(p0,p2,150,1);
draw();
function draw(){
ctx.clearRect(0,0,cw,ch);
ctx.beginPath();
ctx.moveTo(p0.x,p0.y);
ctx.quadraticCurveTo(p1.x,p1.y,p2.x,p2.y);
ctx.strokeStyle='blue';
ctx.lineWidth=3;
ctx.stroke();
dot(p0);
dot(p1);
dot(p2);
dot(midpoint);
dot(perpendicularPoint)
$length.text('Curve length: '+parseInt(QCurveLength(p0,p1,p2)))
}
//
function dot(p){
ctx.beginPath();
ctx.arc(p.x,p.y,radius,0,PI2);
ctx.closePath();
ctx.fillStyle=p.color;
ctx.fill();
ctx.lineWidth=1;
ctx.strokeStyle='black';
ctx.stroke();
}
function setQLength(p0,p2,length,tolerance){
var dx=p2.x-p0.x;
var dy=p2.y-p0.y;
var alength=Math.sqrt(dx*dx+dy*dy);
// impossible to fit
if(alength>length){
alert('The points are too far apart to have length='+length);
return;
}
// fit
for(var distance=0;distance<200;distance++){
// calc the point perpendicular to midpoint at specified distance
var p=pointPerpendicularToMidpoint(p0,p2,distance);
p1.x=p.x;
p1.y=p.y;
// calc the result qCurve length
qlength=QCurveLength(p0,p1,p2);
// draw the curve
draw();
// break if qCurve's length is within tolerance
if(Math.abs(length-qlength)<tolerance){
break;
}
}
return(p1);
}
function pointPerpendicularToMidpoint(p0,p2,distance){
var dx=p2.x-p0.x;
var dy=p2.y-p0.y;
var perpAngle=Math.atan2(dy,dx)-Math.PI/2;
midpoint={ x:p0.x+dx*0.50, y:p0.y+dy*0.50, color:'purple' };
perpendicularPoint={
x: midpoint.x+distance*Math.cos(perpAngle),
y: midpoint.y+distance*Math.sin(perpAngle),
color:'cyan'
};
return(perpendicularPoint);
}
// Attribution: Mateusz Matczak
// http://www.malczak.linuxpl.com/blog/quadratic-bezier-curve-length/
function QCurveLength(p0,p1,p2){
var a={x: p0.x-2*p1.x+p2.x, y: p0.y-2*p1.y+p2.y}
var b={x:2*p1.x-2*p0.x, y:2*p1.y-2*p0.y}
var A=4*(a.x*a.x+a.y*a.y);
var B=4*(a.x*b.x+a.y*b.y);
var C=b.x*b.x+b.y*b.y;
var Sabc=2*Math.sqrt(A+B+C);
var A2=Math.sqrt(A);
var A32=2*A*A2;
var C2=2*Math.sqrt(C);
var BA=B/A2;
if(A2==0 || BA+C2==0){
var dx=p2.x-p0.x;
var dy=p2.y-p0.y;
var length=Math.sqrt(dx*dx+dy*dy);
}else{
var length=(A32*Sabc+A2*B*(Sabc-C2)+(4*C*A-B*B)*Math.log((2*A2+BA+Sabc)/(BA+C2)))/(4*A32)
}
return(length);
};
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4 id=length>Curve length:</h4>
<h4>Red,Gold == start and end points<br>Purple == midpoint between start & end<br>Cyan == middle control point.</h4>
<canvas id="canvas" width=300 height=300></canvas>
答案 2 :(得分:0)
bidAmount