Canvas 上的水平滚动。HTML5

11

我正在一个Web项目中使用JavaScript创建画布。

画布在x-y平面上有图形表示。

我试图为画布添加水平滚动功能。

我已经调查了一些方法:

1)在画布上绘制12个月的数据,当鼠标向前滚动时,第一个月的数据消失,最后添加一个新的月份数据,重新绘制新的画布。

缺点:每次鼠标滚动以浏览时间轴时,都必须进行新的SQL查询,使我的Web应用程序变得非常慢。

2)也许我可以通过1个SQL查询在画布上绘制10年的数据,但只显示12个月的数据,掩盖其余9年的数据。 现在,当客户端滚动时,我捕获滚动事件并移动到画布的适当部分。 是否可能? 如果是,那么该如何实现?

有人能给些建议吗?

我当前表示画布=仅包含12个月的数据

我当前表示的画布=仅包含12个月的数据

更具体地说,我希望我的客户端滚动操作能够像以下小部件一样:

http://www.simile-widgets.org/timeline/


1
SVG通常被认为是数据绘图的更合适工具,只是这么说。 - Shmiddty
但是你想要做的是使用 context.translate(-x, 0) 来模拟这种效果。这样,你就不必更改数据点的 x 坐标了。 - Shmiddty
2个回答

14
这是一个相当基础的实现:http://jsfiddle.net/CQPeU/
var can = document.getElementById("can"),
    ctx = can.getContext('2d'),
    dragging = false,
    lastX = 0,
    translated = 0;

// these two lines will make the y-axis grow upwards. 
ctx.scale(1,-1);  
ctx.translate(0, -400);

can.onmousedown = function(e){
  var evt = e || event;
  dragging = true;
  lastX = evt.offsetX;
}

window.onmousemove = function(e){
  var evt = e || event;
  if (dragging){
    var delta = evt.offsetX - lastX;
    translated += delta;
    ctx.translate(delta, 0);  // translate the context.
    lastX = evt.offsetX;
    draw();  // redraw
  }
}

window.onmouseup = function(){
  dragging = false;
}


function draw() {
  ctx.clearRect(-translated, 0, 600, 400); // this is why we need to keep track of how much we've translated
  for (var i = 0; i < plot.length; i++) {
    ctx.beginPath();
    ctx.arc(plot[i].x, plot[i].y, 5, 0, 2 * Math.PI); // note we don't have to futz with the x/y values, and can use them directly. 
    ctx.fill();
  }
}

要创建一个网格,你可以像这样做:
var grid = (function(dX, dY){
  var can = document.createElement("canvas"),
      ctx = can.getContext('2d');
  can.width = dX;
  can.height = dY;
  // fill canvas color
  ctx.fillStyle = 'black';
  ctx.fillRect(0, 0, dX, dY);

  // x axis
  ctx.strokeStyle = 'orange';
  ctx.moveTo(.5, 0.5);
  ctx.lineTo(dX + .5, 0.5);
  ctx.stroke();

  // y axis
  ctx.moveTo(.5, .5);
  ctx.lineTo(.5, dY + .5);
  ctx.stroke();

  return ctx.createPattern(can, 'repeat');
})(100, 50);

以下是使用方法:

function draw() {
  ctx.clearRect(-translated, 0, 600, 400);
  ctx.rect(-translated, 0, 600, 400);
  ctx.fillStyle = grid;
  ctx.fill();
  ctx.fillStyle = "#fff";
  for (var i = 0; i < plot.length; i++) {
    ctx.beginPath();
    ctx.arc(plot[i].x, plot[i].y, 5, 0, 2 * Math.PI);
    ctx.fill();
  }
}

演示更新: http://jsfiddle.net/CQPeU/2/


采用这种方法,一个新问题浮现出来 - y轴上的静态标签。 例如:-http://jsfiddle.net/WNpKE/10/ - Philo
我建议将它们放在单独的图层上(不同的画布或作为普通的DOM元素)。 - Shmiddty
2
或者,也许更简单的方法是将它们的x坐标设置为“-translated”,如此处所示:http://jsfiddle.net/WNpKE/11/。 - Shmiddty
这很棒!!! 然而,我的方法存在问题,就是背景网格没有复制……它仅限于 x = 65 和 x = 930之间//你知道我在说什么吗?我想结合你的无限网格方法。那将是我的解决方案!!! - Philo
1
我的天啊!一个丹尼尔已经来审判了。这将使我朝着正确的方向前进。非常感谢你,伙计!如果你在身边,我会请你吃午餐、晚餐和喝啤酒。 - Philo

3
为了避免在每个鼠标移动事件中重新绘制,您可以将整个画布超大地绘制,然后更改CSS margin属性。当画布的内容变得更加复杂时,这是一个显著的性能提升。
以下是演示:https://jsfiddle.net/ax7n8944/ HTML:
<div id="canvasdiv" style="width: 500px; height: 250px; overflow: hidden">
    <canvas id="canvas" width="10000px" height="250px"></canvas>
</div>

JS:

var canvas = document.getElementById("canvas");
var context = canvas.getContext('2d');
var dragging = false;
var lastX;
var marginLeft = 0;

for (var i = 0; i < 1000; i++) {
    context.beginPath();
    context.arc(Math.random() * 10000, Math.random() * 250, 20.0, 0, 2 * Math.PI, false);
    context.stroke();
} 

canvas.addEventListener('mousedown', function(e) {
    var evt = e || event;
    dragging = true;
    lastX = evt.clientX;
    e.preventDefault();
}, false);

window.addEventListener('mousemove', function(e) {
    var evt = e || event;
    if (dragging) {
        var delta = evt.clientX - lastX;
        lastX = evt.clientX;
        marginLeft += delta;
        canvas.style.marginLeft = marginLeft + "px";
    }
    e.preventDefault();
}, false);

window.addEventListener('mouseup', function() {
    dragging = false;
}, false);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接