【问题标题】:HTML Canvas Zoom to region on graphHTML Canvas 缩放到图形上的区域
【发布时间】:2017-01-03 15:39:03
【问题描述】:

只是想知道有没有人有任何解决方案来放大图表上的一个区域。当我说区域时,我的意思是,您在画布上的某个点向下移动鼠标,在仍然按下的同时将鼠标移动到另一个位置,当鼠标向上时,我想放大该块。并且只缩放 x 轴,没有 y。

这正是我想做的——http://canvasjs.com/docs/charts/basics-of-creating-html5-chart/zooming-panning/

现在我可以放大了,但放大后我的图表一团糟。线和点都被拉伸、扭曲等,我尝试使用比例进行调整。

这是我迄今为止一直在做的事情。

let y = heightOfCanvas / 2,

    // Get the region in pixels to zoom to. If canvas width
    // is 500, this is lets say 100px to 400px. 
    regionToZoom = toX - fromX,

    // Subtract from actual width, so we get what will be scaled
    // out.
    difference = widthOfCanvas - regionToZoom,

    // Scale = fullWidth / partWidth
    scale = widthOfCanvas / difference,

    // This is how I worked out the value to translate back,
    // so that it lines up right
    translateBack = widthOfCanvas * scale



    ctx.translate(from, y);

    ctx.scale(scale, 1);

    from = from * translateBack;

    ctx.translate(-from, -y);

    // REDRAW GRAPH AFTER

像这样放大得很好,但正如我所说,之后它变得一团糟。

如果有人过去做过类似的事情,比如上面的例子,我会非常感激你的帮助。

肖恩。

【问题讨论】:

标签: javascript html canvas


【解决方案1】:

这是一个快速示例,展示了如何选择和显示图表的子集

// canvas vars
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
ctx.font='14px arial';
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(); }
window.onresize=function(e){ reOffset(); }

var isDrilled=false;
var isDown=false;
var startX,startY;

// graph vars
var axisY=ch/3;
var points=[];

// DEMO: add random data points
var demoPointCount=50;
for(var i=0;i<demoPointCount;i++){
    points.push({y:Math.random()*150-75});
}

// draw the full data graph
draw(points,axisY);

// listen to mouse events
window.onmousedown=(function(e){handleMouseDown(e);});
window.onmousemove=(function(e){handleMouseMove(e);});
window.onmouseup=(function(e){handleMouseUpOut(e);});
window.onmouseout=(function(e){handleMouseUpOut(e);});


function draw(pts,axisY,startX,mouseX,drillStart,drillEnd){
    // redraw the given pts data
    ctx.clearRect(0,0,cw,ch);
    ctx.beginPath();
    ctx.moveTo(0,pts[0].y+axisY);
    for(var i=0;i<pts.length;i++){
        var x=cw/(pts.length-1)*i;
        ctx.lineTo(x,pts[i].y+axisY);
    }
    ctx.stroke();
    // used when highlighting a drilldown section of full data
    if(startX && mouseX){
        ctx.globalAlpha=0.10;
        ctx.fillStyle='black';
        ctx.fillRect(startX,0,mouseX-startX,ch);
        ctx.globalAlpha=1.00;
    }else if(drillStart && drillEnd){
        ctx.fillText('Viewing '+drillStart+' - '+drillEnd+'. Click to return to all data view.',10,20);
    }else{
        ctx.fillText('Drag to select data to drill into.',10,20);
    }
}

function handleMouseDown(e){
  // if displaying drilled data, return to full data
  if(isDrilled){
      isDrilled=false;
      draw(points,axisY);
      return;
  }
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  // start mouse position
  startX=parseInt(e.clientX-offsetX);
  startY=parseInt(e.clientY-offsetY);
  // Put your mousedown stuff here
  isDown=true;
}

function handleMouseUpOut(e){
  if(!isDown){return;}
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  // mouse position
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  // Put your mouseup stuff here
  isDown=false;
  isDrilled=true;
  // normalize
  if(mouseX<startX){ var t=startX; startX=mouseX; mouseX=t; }
  // fetch highlighted start & end data points
  drillStart=parseInt(startX/cw*points.length);
  drillEnd=parseInt(mouseX/cw*points.length);
  var subset=points.slice(drillStart,drillEnd+1);
  // draw the data subset
  draw(subset,axisY,null,null,drillStart,drillEnd);
}

function handleMouseMove(e){
  if(!isDown){return;}
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();
  // mouse position
  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);
  // Put your mousemove stuff here
  draw(points,axisY,startX,mouseX);
}
body{ background-color:white; }
#canvas{border:1px solid red; }
&lt;canvas id="canvas" width=512 height=512&gt;&lt;/canvas&gt;

[以前过时的答案]

这是一种无需记住转换而大惊小怪的缩放方法

这是一个快速的想法,因此可能需要一些修补。

  1. 通过在第二个内存画布上绘制来复制图形:

    var memCanvas=myCanvas.cloneNode();
    var memCtx=memCanvas.getContext('2d');
    memCtx.drawImage(myCanvas,0,0);
    
  2. 将 memCanvas 绘制到显示的画布上:

    context.drawImage(memCanvas,0,0);
    
  3. 鼠标按下时,抓住选择框的左上角坐标。

  4. 在 mouseup 上,抓住选择框的右下角坐标。 注意:如果 top>bottom 或 left>right,您可能需要上下翻转或左右翻转。

  5. 计算仅将选择框全帧绘制到可见画布中所需的缩放因子:

    var scale = Math.min(
        (myCanvas.width/selectionboxWidth),   
        (myCanvas.height/selectionboxHeight)
    );
    
  6. 使用drawImage 的剪切形式从memCanvas 中拉出选择框并将其按比例绘制到可见画布中:

    var x=sboxLeft;
    var y=sboxTop;
    var w=sboxRight-sboxLeft; 
    var h=sboxBottom-sboxTop;
    sboxcontext.drawImage(
        memCanvas,            // fetch from the memCanvas
        x,y,w,h,              // clip the selected box
        0,0,w*scale,h*scale); // scale the selected box into the visible canvas
    

然后,当您想要取消缩放时,您只需将完整的 memCanvas 重新绘制到可见画布(无需取消转换)。生成的图表不是一团糟!

context.drawImage(memCanvas,0,0);  // unzoomed -- simply!

【讨论】:

  • 谢谢马克,我现在要试试这个,你知道我怎么过吗!
  • 对,所以我已经尝试过了,它工作正常,但是,图像本身是缩放的,而我试图保持原始线宽。而且我的图表必须是交互式的,这样当用户将鼠标悬停在一条线上时,它会有点阴影,但我似乎无法用绘图图像做到这一点?
  • 糟糕!当我在您的标题中看到“缩放”时,我假设您想要缩放 - 就像放大图表的一部分。好的,要“深入”到图表的某个子部分,您需要重新绘制图表。 1. 使用 context.scale(scale,scale) 放大画布(在 X 和 Y 方向上缩放相同的因子),2. 定义,但不要描边/填充图形,3. 使用 context.scale(-scale,-scale) 取消缩放,4. 现在描边/充满。结果是图形将被缩放,但线条将不被缩放。
  • 关于交互性。浏览器总是报告未转换空间中的鼠标坐标,因此当您呈现缩放图形时,您必须将浏览器的鼠标坐标乘以您使用的缩放因子:mouseX*=scale; mouseY*=scale;
  • 叹息,所以我刚刚读到您想要 scaleX 而不是 scaleY。然后忽略我之前关于context.scale 的评论。根本不要使用context.scale。只需将图形的 x 坐标乘以所需的比例因子即可。
猜你喜欢
  • 1970-01-01
  • 2015-12-21
  • 2019-05-04
  • 1970-01-01
  • 2020-07-24
  • 2019-10-08
  • 1970-01-01
  • 2012-04-30
  • 2012-01-25
相关资源
最近更新 更多