目的:为了将一些数据点集数据展示到页面上,可以使用鼠标缩放和拖动;
是不是有点抽象,那,可以想象一下百度/高德地图上的效果,绘制了一些地形和路标:
- 拖动:可以查看相邻位置和更远位置的地形;
- 缩放:可以更清晰的查看当前位置。
当然了,我要做的并没有地图那么完美和复杂,但是麻雀虽小,该有的基本逻辑也都是有的,今下来,记录一下开发(研究)过程:
【1】 绘制一个支持拖动的球 (gif图没录好,就先用静态图吧)
效果图:
关键点分析:
- 利用 canvas.getContext(“2d”)获取2D模型上下文ctx;
- 利用ctx.arc()方法绘制圆形;
- onmousedown,onmousemove, onmouseout分别鼠标的按下事件,移动事件,移开事件。分别捕获按下事件和移动事件产生的x,y坐标(dx, dy // mx, my ),计算偏移量mx - dx , my - dy。
- 鼠标移开事件时,重置鼠标事件。
- 每次绘制之前,都要清空画布区域ctx.clearRect()
代码 (只粘贴了body段,其他部分用模板就行了):
<body>
<canvas id="can" width="400" height="400"></canvas>
<div>1 不管是否点中红球,都能拖动; 2 无缩放</div>
<script type="text/javascript">
var can = document.getElementById("can");
var ctx = can.getContext("2d");
var isDown = false;
var dx = 0, dy = 0; // 鼠标按下位置的坐标
var centerx = 50, centery = 50; // 圆心坐标
// 创建圆, 圆心坐标: x,y
function createBlock(x, y) {
ctx.clearRect(0, 0, can.width, can.height);
ctx.beginPath();
ctx.fillStyle = "red";
ctx.arc(x, y, 30, 0, Math.PI * 2);
ctx.fill();
}
// 鼠标按下,将鼠标按下坐标保存在x,y中
createBlock(centerx, centery);
can.onmousedown = function(ev) {
var e = ev || event;
dx = e.clientX;
dy = e.clientY;
isDown = true;
can.onmousemove = function(ev) {
if (isDown) {
var e = ev || event;
var mx = e.clientX;
var my = e.clientY;
var _x = mx - dx; // 偏移量x
var _y = my - dy; // 偏移量y
createBlock((centerx + _x), (centery + _y));
}
};
//鼠标移开事件
can.onmouseup = function(ev) {
isDown = false;
var e = ev || event;
centerx += (e.clientX - dx);
centery += (e.clientY - dy);
// 重置
can.onmousemove = null;
can.onmouseup = null;
};
};
</script>
</body>
只能拖动并不能满足一开始的需求,所以还要添加缩放,缩放的话,单独处理的话,很容易,但和拖动一同处理的话,还是有坑的,这车还是需要谨慎驾驶。
【1】 绘制一个支持拖动的球 (gif图没录好,就先用静态图吧)
效果图:
没有gif图,就先不贴了,以后录好了再替换上。
关键点分析:
- 绘制图形的方法里,需要缩放和拖动一同处理,即:便宜量和缩放比率的处理。
- 使用三个按钮(放大,还原,缩小)开放缩放功能,
代码 (只粘贴了body段,其他部分用模板就行了):
<body>
<canvas id="can" width="800" height="500"></canvas>
<div>>
<button id="btn_in">放大</button>
<button id="btn_normal">还原</button>
<button id="btn_out">缩小</button>
</div>
<div>1 不管是否点中红球,都能拖动; 2 包含缩放和拖动</div>
<div id="posdiv"></div>
<div> 圆心坐标:
<div id="centerdiv"></div>
</div>
<script type="text/javascript">
var can = document.getElementById("can");
var ctx = can.getContext("2d");
var isDown = false;
var dx = 0,
dy = 0; // 鼠标按下位置的坐标
var offx = 0, offy = 0; // 拖动的距离,x,y
var centerx = 50,
centery = 50; // 圆心坐标
var cx = 0, cy = 0; // 画布中心点坐标
// 原始比率(scaler = 1)时,拖动产生的偏移量
var _x = 0, _y = 0; // 这个变量用来计算缩放后的偏移量误差
var scaler = 1; // 缩放比率
/**
* 创建圆滑块: 圆心坐标 x,y
*/
function createBlock(fished) {
/**
* 画布中心点 + (中心点与圆心间距*缩放系数) + 偏移量
*/
var nx = cx + (centerx-cx)*scaler + offx;
var ny = cy + (centery-cy)*scaler + offy;
ctx.clearRect(0, 0, can.width, can.height);
ctx.beginPath();
ctx.save();
// ctx.translate(cx, cy);
ctx.fillStyle = "red";
console.log("圆心坐标="+centerx+","+centery+"画布中心点="+cx+", "+cy);
ctx.arc(nx, ny, 30*scaler, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
//鼠标按下,将鼠标按下坐标保存在x,y中
can.onmousedown = function(ev) {
var e = ev || window.event;
dx = e.clientX;
dy = e.clientY;
isDown = true;
can.onmousemove = function(ev) {
$("#posdiv").html("鼠标坐标,x="+ev.clientX+", y="+ev.clientY);
if (isDown) {
var e = ev || window.event;
var mx = e.clientX;
var my = e.clientY;
offx = mx - dx;
offy = my - dy;
createBlock();
}
};
//鼠标移开事件
can.onmouseup = function(ev) {
isDown = false;
var e = ev || window.event;
createBlock(true);
// 重置
centerx += (e.clientX - dx)/scaler;
centery += (e.clientY - dy)/scaler;
offx = 0;
offy = 0;
can.onmousemove = null;
can.onmouseup = null;
};
};
var mouseHandler = function(ev) {
if (ev.type == "mousewheel") {
var e = (ev || window.event).originalEvent;
var deltaY = e.wheelDelta;
if(deltaY == 120) {
$("#btn_in").trigger("click");
} else if(deltaY == -120) {
$("#btn_out").trigger("click");
}
}
};
$(can).off().on({
mousewheel : mouseHandler,
});
$(function(){
cx = $("canvas")[0].width / 2;
cy = $("canvas")[0].height / 2;
createBlock();
$("#btn_in").off().on({
"click": function() {
scaler += 0.5;
createBlock();
}
});
$("#btn_normal").off().on({
"click": function() {
scaler = 1;
_x = 0, _y = 0;
createBlock();
}
});
$("#btn_out").off().on({
"click": function() {
if (scaler <=1 ){
$.toast("alreay smallest, cannot zoom out!");
return;
}
scaler -= 0.5;
createBlock();
}
});
})
</script>
</body>
注意:
1 这个带缩放版本的代码比只能拖动的代码,多做了一些优化,并使用jQuery(因为我是通过jQuery开始使用js的,jQuery比原生js简单方便很多啊):
2 在完成这个demo版本的功能过程中,我也是查询了很多资料,并咨询了同事,所以还要在what 的基础研究how 和 why;
3 注释我觉得很全了,有问题的话,评论里咱们共同探讨。
4 闭包我还用不好,所以很尴尬的是,我没有像同事一样使用闭包封装,定义了一堆丑陋的成员变量。
5 当然是代码啦,点我