如何从画布中的复杂三角形放大/缩小

时间:2016-04-12 12:24:42

标签: javascript html css canvas

嘿,我创造了一种非常复杂的三角形,其中包含7个片段和标签以供指导。 我正在尝试为当前形状添加放大/缩小功能。 我已经使用缩放方法编写了放大方法,问题是当我放大或缩小三角形时,每个东西都会分崩离析,我无法正确缩放。 我对这个问题的建议是: 1.将所有元素组合成一个元素,然后放大或缩小统一元素。我在画布上做了吗? 2.缩小到特定的中间绝对点,我该怎么办? 任何帮助都将得到祝福,提前谢谢: - )。

放大/缩小方法:

$('#zoomIn').click(function() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.scale(1/0.9, 1/0.9);
  drawDuvalShape();
});

$('#zoomOut').click(function() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.scale(0.90, 0.90);
  drawDuvalShape();
});

放大: enter image description here



$(function(){
  
//offset : 150+ : x & 50+ : y.  
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");

// https://www.researchgate.net/publication/4345236_A_Software_Implementation_of_the_Duval_Triangle_Method

var v0={x:154,y:344};
var v1={x:302,y:69};
var v2={x:450,y:344};
var triangle=[v0,v1,v2];

// Define all your segments here
var segments=[
  { 
    points:[{x:154,y:344},{x:184,y:344},{x:236,y:246},{x:222,y:219}],
    fill:'rgb(172,236,222)',
    label:{text:'O',cx:200,cy:290,withLine:false,endX:null,endY:null}
  },
  { 
    points:[{x:236,y:246},{x:222,y:219},{x:280,y:111},{x:295,y:136}],
    fill:'deepskyblue',
    label:{text:'S',cx:258,cy:180,withLine:false,endX:null,endY:null}
  },
  { 
    points:[{x:280,y:111},{x:295,y:136},{x:317,y:96},{x:302,y:69}],
    fill:'rgb(172,236,222)',
    label:{text:'O',cx:300,cy:110,withLine:false,endX:305,endY:120}
  },
  { 
    points:[{x:184,y:344},{x:258,y:344},{x:310,y:248},{x:273,y:179},],
    fill:'navajoWhite',
    label:{text:'ND',cx:260,cy:280,withLine:false,endX:null,endY:null}
  },
  { 
    points:[{x:258,y:344},{x:310,y:248},{x:360,y:344}],
    fill:'#8CAAD2',
    label:{text:'T3',cx:310,cy:310,withLine:false,endX:null,endY:null}
  },
  { 
    points:[{x:360,y:344},{x:273,y:179},{x:300,y:130},{x:355,y:232},{x:353,y:236},{x:386,y:300}],
    fill:'peru',
    label:{text:'C',cx:320,cy:220,withLine:false,endX:null,endY:null}
  },
   { 
    points:[{x:360,y:344},{x:450,y:344},{x:353,y:167},{x:337,y:198},{x:355,y:232},{x:353,y:236},{x:386,y:300}],
    fill:'#8CAAD2',
    label:{text:'T3',cx:400,cy:310,withLine:false,endX:null,endY:null}
  },
   { 
    points:[{x:337,y:198},{x:353,y:167},{x:317,y:96},{x:300,y:128}],
    fill:'#918CD2',
    label:{text:'T2',cx:328,cy:150,withLine:false,endX:null,endY:null}
  },
  { 
    points:[{x:280,y:111},{x:283,y:113},{x:291,y:96},{x:288,y:96}],
    fill:'#d2b48c',
    label:{text:'PD',cx:250,cy:86,withLine:true,endX:295,endY:96}
  },
  { 
    points:[{x:291,y:96},{x:288,y:96},{x:299,y:75},{x:302,y:78}],
    fill:'#d2b48c',
    label:{text:'PD',cx:250,cy:86,withLine:true,endX:295,endY:96}
  }
  
];

  // label styles
  var labelfontsize=12;
  var labelfontface='verdana';
  var labelpadding=3;

  // pre-create a canvas-image of the arrowhead
  var arrowheadLength=10;
  var arrowheadWidth=8;
  var arrowhead=document.createElement('canvas');
  premakeArrowhead();

  var legendTexts=['PD   =  Partial Discharge',
                   'T2    =  Thermal fault 300 ℃ < T < 700 ℃',
                   'T3    =  Thermal fault  T > 700 ℃',
                    'O     =  Overheating < 250ºC',
                    'S     =   Stray Gassing of Oil',
                    'ND  =  Not Determined',
                    'C     =  Carbonization'];

drawDuvalShape();
  
function drawDuvalShape(){
// start drawing
/////////////////////

// draw colored segments inside triangle
for(var i=0;i<segments.length;i++){
  drawSegment(segments[i]);
}
// draw ticklines
ticklines(v0,v1,9,0,20);
ticklines(v1,v2,9,Math.PI*3/4,20);
ticklines(v2,v0,9,Math.PI*5/4,20);
// molecules
moleculeLabel(v0,v1,100,Math.PI,'% CH4');
moleculeLabel(v1,v2,100,0,'% C2H4');
moleculeLabel(v2,v0,75,Math.PI/2,'% C2H6');
// draw outer triangle
drawTriangle(triangle);
// draw legend
drawLegend(legendTexts,10,10,12.86);

  // end drawing
/////////////////////
}

function drawSegment(s){
  // draw and fill the segment path
  ctx.beginPath();
  ctx.moveTo(s.points[0].x,s.points[0].y);
  for(var i=1;i<s.points.length;i++){
    ctx.lineTo(s.points[i].x,s.points[i].y);
  }
  ctx.closePath();
  ctx.fillStyle=s.fill;
  ctx.fill();
  ctx.lineWidth=2;
  ctx.strokeStyle='black';
  ctx.stroke();
  // draw segment's box label
  if(s.label.withLine){
    lineBoxedLabel(s,labelfontsize,labelfontface,labelpadding);
  }else{
    boxedLabel(s,labelfontsize,labelfontface,labelpadding);
  }
}


function moleculeLabel(start,end,offsetLength,angle,text){
  ctx.textAlign='center';
  ctx.textBaseline='middle'
  ctx.font='14px verdana';
  var dx=end.x-start.x;
  var dy=end.y-start.y;
  var x0=parseInt(start.x+dx*0.50);
  var y0=parseInt(start.y+dy*0.50);
  var x1=parseInt(x0+offsetLength*Math.cos(angle));
  var y1=parseInt(y0+offsetLength*Math.sin(angle));
  ctx.fillStyle='black';
  ctx.fillText(text,x1,y1);
  // arrow
  var x0=parseInt(start.x+dx*0.35);
  var y0=parseInt(start.y+dy*0.35);
  var x1=parseInt(x0+50*Math.cos(angle));
  var y1=parseInt(y0+50*Math.sin(angle));
  var x2=parseInt(start.x+dx*0.65);
  var y2=parseInt(start.y+dy*0.65);
  var x3=parseInt(x2+50*Math.cos(angle));
  var y3=parseInt(y2+50*Math.sin(angle));
  ctx.beginPath();
  ctx.moveTo(x1,y1);
  ctx.lineTo(x3,y3);
  ctx.strokeStyle='black';
  ctx.lineWidth=1;
  ctx.stroke();
  var angle=Math.atan2(dy,dx);
  ctx.translate(x3,y3);
  ctx.rotate(angle);
  ctx.drawImage(arrowhead,-arrowheadLength,-arrowheadWidth/2);
  ctx.setTransform(1,0,0,1,0,0);
}


function boxedLabel(s,fontsize,fontface,padding){
  var centerX=s.label.cx;
  var centerY=s.label.cy;
  var text=s.label.text;
  ctx.textAlign='center';
  ctx.textBaseline='middle'
  ctx.font=fontsize+'px '+fontface
  var textwidth=ctx.measureText(text).width;
  var textheight=fontsize*1.286;
  var leftX=centerX-textwidth/2-padding;
  var topY=centerY-textheight/2-padding;
  ctx.fillStyle='white';
  ctx.fillRect(leftX,topY,textwidth+padding*2,textheight+padding*2);
  ctx.lineWidth=1;
  ctx.strokeRect(leftX,topY,textwidth+padding*2,textheight+padding*2);
  ctx.fillStyle='black';
  ctx.fillText(text,centerX,centerY);
}


function lineBoxedLabel(s,fontsize,fontface,padding){
  var centerX=s.label.cx;
  var centerY=s.label.cy;
  var text=s.label.text;
  var lineToX=s.label.endX;
  var lineToY=s.label.endY;
  ctx.textAlign='center';
  ctx.textBaseline='middle'
  ctx.font=fontsize+'px '+fontface
  var textwidth=ctx.measureText(text).width;
  var textheight=fontsize*1.286;
  var leftX=centerX-textwidth/2-padding;
  var topY=centerY-textheight/2-padding;
  // the line
  ctx.beginPath();
  ctx.moveTo(leftX,topY+textheight/2);
  ctx.lineTo(lineToX,topY+textheight/2);
  ctx.strokeStyle='black';
  ctx.lineWidth=1;
  ctx.stroke();
  // the boxed text
  ctx.fillStyle='white';
  ctx.fillRect(leftX,topY,textwidth+padding*2,textheight+padding*2);
  ctx.strokeRect(leftX,topY,textwidth+padding*2,textheight+padding*2);
  ctx.fillStyle='black';
  ctx.fillText(text,centerX,centerY);
}


function ticklines(start,end,count,angle,length){
  var dx=end.x-start.x;
  var dy=end.y-start.y;
  ctx.lineWidth=1;
  for(var i=1;i<count;i++){
    var x0=parseInt(start.x+dx*i/count);
    var y0=parseInt(start.y+dy*i/count);
    var x1=parseInt(x0+length*Math.cos(angle));
    var y1=parseInt(y0+length*Math.sin(angle));
    ctx.beginPath();
    ctx.moveTo(x0,y0);
    ctx.lineTo(x1,y1);
    ctx.stroke();
    if(i==2 || i==4 || i==6 || i==8){
      var labelOffset=length*3/4;
      var x1=parseInt(x0-labelOffset*Math.cos(angle));
      var y1=parseInt(y0-labelOffset*Math.sin(angle));
      ctx.fillStyle='black';
      ctx.fillText(parseInt(i*10),x1,y1);
    }
  }
}


function premakeArrowhead(){
  var actx=arrowhead.getContext('2d');
  arrowhead.width=arrowheadLength;
  arrowhead.height=arrowheadWidth;
  actx.beginPath();
  actx.moveTo(0,0);
  actx.lineTo(arrowheadLength,arrowheadWidth/2);
  actx.lineTo(0,arrowheadWidth);
  actx.closePath();
  actx.fillStyle='black';
  actx.fill();
}


function drawTriangle(t){
  ctx.beginPath();
  ctx.moveTo(t[0].x,t[0].y);
  ctx.lineTo(t[1].x,t[1].y);
  ctx.lineTo(t[2].x,t[2].y);
  ctx.closePath();
  ctx.strokeStyle='black';
  ctx.lineWidth=2;
  ctx.stroke();
}


function drawLegend(texts,x,y,lineheight){
  ctx.textAlign='left';
  ctx.textBaseline='top';
  ctx.fillStyle='black';
  ctx.font='12px arial';
  for(var i=0;i<texts.length;i++){
    ctx.fillText(texts[i],x,y+i*lineheight);
  }
}
  $('#zoomIn').click(function() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.scale(1/0.9, 1/0.9);
  drawDuvalShape();
});

$('#zoomOut').click(function() {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.scale(0.90, 0.90);
  drawDuvalShape();
});
})
&#13;
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red; margin:0 auto; }
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<canvas id="canvas" width=650 height=500></canvas>
 <button id="zoomIn">+</button>
 <button id="zoomOut">-</button>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:1)

渲染代码在绘图期间重置变换:

ctx.setTransform(1,0,0,1,0,0);

这意味着在绘图过程中将覆盖缩放变换。

moleculeLabel()方法的末尾找到此代码段:

  ...
  var angle=Math.atan2(dy,dx);
  ctx.translate(x3,y3);
  ctx.rotate(angle);
  ctx.drawImage(arrowhead,-arrowheadLength,-arrowheadWidth/2);
  ctx.setTransform(1,0,0,1,0,0);
}

并改为:

  var angle=Math.atan2(dy,dx);
  ctx.save();                       // save
  ctx.translate(x3,y3);
  ctx.rotate(angle);
  ctx.drawImage(arrowhead,-arrowheadLength,-arrowheadWidth/2);
  ctx.restore();                    // restore
}

这将保留全球变换。可能会有更多实例,如果是,请重复。

关于缩放中心(从original answer here添加回来):

缩放将始终发生在原点(其中(0,0)最初位于上下文的左上角),因此为了能够在特定点进行缩放,您需要做的就是首先转换为所需的缩放中心,应用缩放,翻译(在这种情况下)和绘制。

简化此操作的一种方法是在形状的预定义中心点(0,0)周围定义三角形和形状,以便形状中的所有点都相对于此。

答案 1 :(得分:0)

使用ctx.scale。 ctx.scale缩放ypu在画布上绘制的所有内容。唯一的缺点是,它只能从左上角扩展。

这可以通过首先对画布进行描绘来解决,因此图像的中间位于左上角,缩放,然后向后变换,并绘制三角形。