javascript - 当长度超过时,将直线变为曲线

标签 javascript html canvas

我想在 Canvas 中将几条腿显示为矩形。 基于将我的腿的英里数分组的数组,我制定了算法以在给定的 Canvas 上按比例表示它们。

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);
<canvas id="myCanvas" height="200" width="500"></canvas>

然后,我想把then以矩形的形式显示出来,做一个循环(下面不是矩形,但是可以帮助大家理解):

enter image description here

我已经尝试操纵 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);
<canvas id="myCanvas" height="2000" width="2000"></canvas>

最后,我创建了当我知道如何弯曲我的线条并保持其长度时需要的片段!。我已经计算了我的 Canvas 表面的周长,以便重新计算我的腿的比例。

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);
<canvas id="myCanvas" height="200" width="500"  style="border:1px solid #000000;"></canvas>

我已经对我的所有代码进行了注释,以帮助您理解逻辑和我想要的内容。

那么,是否有可能以通用方式弯曲线条?

最佳答案

我可能会做这样的事情:

  • 定义一个包含基于分辨率的条目数的保持数组
  • 将线映射到该数组设置 1's 非常会有一个线范围,0's 用于间隙。
  • 定义一个目标形状,例如椭圆形(实际上可以是任何形状!),它由与阵列分辨率相同的部分组成。将每个部分及其坐标存储在一个数组中(与线数组长度相同)。
  • 使用形状数组和线数组之间的插值对每个部分进行变形

现在您可以将线条制作成您想要的几乎任何形状和形式。

提示:您当然可以通过第一次直接映射来跳过一个形状。
技巧 2:可以在标准化坐标中定义形状,这样可以更轻松地平移和缩放它们。

例子

在这里我们定义了一个圆 Angular 正方形和圆形,然后将线条映射到其中一个上,我们可以在形状之间变形以找到我们喜欢的组合并使用它(注意:因为这个例子中的正方形从“右上角”开始 Angular 而不是圆圈为 0° 的地方也会有一个小的旋转,这可以作为练习单独处理)。

就此而言,圆 Angular 正方形可能是一只兔子(对于更“紧凑”的圆 Angular 正方形,您可以使用立方贝塞尔曲线而不是此处的二次方)。关键是可以独立于线条本身来定义形状。这可能有点矫枉过正,但它并不复杂,而且用途广泛,即。通用。

参见 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>

关于javascript - 当长度超过时,将直线变为曲线,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30596861/

相关文章:

javascript - jQuery,如何设置多个输入需要通知输入

javascript - 如何使用计时器删除嵌入 html 中的 swf 文件

css - 在 html5 Canvas 上添加 ul li 图像

javascript - 管理要在 HTML5 Canvas 上绘制的二维数组中的文本映射

javascript - 使用 String.toLowerCase 进行 Typescript 字符串比较

javascript - JS 音频不工作

html - 如何删除 youtube iframe 中的空白区域?

javascript - Canvas 另存为图像 [canvg - 在 Canvas 上渲染 svg]

javascript - 将跨度数组的第一个索引和最后一个索引分配给对象

html - 使用 HTML 对齐和对齐文本