Reputation: 1297
Hey I have created some kind of very complex triangle which contains 7 segments and labels for guidance. I am trying to add zoom in/out capabilities to current shape. I have wrote the zoom in out methods using scale method, the problem is that when I zoom in or out of the triangle, every thing is falling apart and I can't zoom properly. My suggestion to this problem is: 1.Group up all the elements into one element, and then zoom in or out on the united element.can I do it on canvas? 2.Zoom into a specific middle absolute point, how can I do it? Any help will be blessed, thanks in advance :-).
The zoom in/out methods:
$('#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();
});
$(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();
});
})
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red; margin:0 auto; }
<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>
Upvotes: 1
Views: 92
Reputation:
The render code is resetting transforms during drawing:
ctx.setTransform(1,0,0,1,0,0);
This means your transforms with zoom will be overridden during the drawing process.
Locate this code segment at the end of the moleculeLabel()
method:
...
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);
}
and change to:
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
}
this will preserve global transforms. There may be more instances and if so, just repeat.
About center of zoom (added back from my original answer here):
Scale will always happen at the origin point (where (0,0) is, initially in the upper left corner of context) so to be able to zoom at a specific point, all you need to do is to first translate to the desired zoom center, apply scale, translate back (in this case) and draw.
One way to simplify this is to define the triangles and shapes around a predefined center point (0,0) of the shape so all points in the shape is relative to this.
Upvotes: 1
Reputation: 4039
Use ctx.scale. ctx.scale Scales everything what ypu draw on the canvas. The only down-side, that it only scales from the top-left corner.
This can be solved though, by first trabslating the canvas, so the middle of your image goes up to the left corner, scale it, then transform back, and draw yor triangle.
Upvotes: 0