文章的代码和思路来自https://blog.csdn.net/cysear/article/details/89185239#comments_13156603
在原文基础上做了部分优化:
- 实现了不拖拽也可缩放的效果
- 双击鼠标,在图片上生成一个圆圈且该圆圈实现了跟随缩放比例一起缩放以及缩放后位置不变的效果
效果图不会上传。。自己下下来看吧
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>canvas.js</title>
<style>
#mouseTip{
display:none;
position:absolute;
pointer-events: none;
background-color: gray;
}
</style>
</head>
<body>
<canvas id="scaleDragCanvas" width="800" height="600" style="border: thin solid #aaaaaa; margin:0 auto;
display: block;"></canvas>
<span id='mouseTip'>
</span>
<script>
var canvas, context;
var showTips = null, originalPos = null;
var img, imgX = 0, imgY = 0, imgScale = 1, scaleRate = 1;
var MINIMUM_SCALE = 1 ,pos={},posl={},dragging = false;
(function int() {
canvas = document.getElementById('scaleDragCanvas'); //画布对象
context = canvas.getContext('2d');//画布显示二维图片
loadImg();
canvasEventsInit();
})();
var initWidth = 0, initHeight = 0
function loadImg() {
img = new Image();
img.onload = function () {
scaleRate = img.width/canvas.width;//让长宽保持相同缩放比例
//MINIMUM_SCALE = canvas.width/img.width;
initWidth = img.width * scaleRate;
initHeight = img.height * scaleRate;
console.log("initWidth=" + initWidth + ",initHeight=" + initHeight + ", scaleRate=" + scaleRate);
drawImage();
//imgScale = scaleRate;
}
img.src = 'F:/image.jpg';
}
function drawImage() {
context.clearRect(0, 0, canvas.width, canvas.height);
// 保证 imgX 在 [img.width*(1-imgScale),0] 区间内
///**
if(imgX<img.width*(1-imgScale) /scaleRate) {
imgX = img.width*(1-imgScale)/scaleRate ;
}else if(imgX>0) {
imgX=0
}
// 保证 imgY 在 [img.height*(1-imgScale),0] 区间内
if(imgY<img.height*(1-imgScale)/scaleRate) {
imgY = img.height*(1-imgScale)/scaleRate;
}else if(imgY>0) {
imgY=0
}
//*/
//console.log(img.width);
context.drawImage(
img, //规定要使用的图像、画布或视频。
0, 0, //开始剪切的 xy 坐标位置。
initWidth, initHeight, //被剪切图像的高度。
//img.width,img.height,
imgX, imgY,//在画布上放置图像的 x 、y坐标位置。
img.width * imgScale, img.height * imgScale //要使用的图像的宽度、高度
//1700,1200
);
//var p = windowToCanvas(150/img.width * imgScale, 400/img.width * imgScale);
//console.log(p.x + "," + p.y);
//console.log("imgX=" + imgX + ",imgY=" + imgY);
//console.log(img.width/canvas.width);
//console.log("========" + img.width + "," + img.height);
//console.log("##" + img.width * (1-imgScale) + "," + img.height * (1-imgScale) + "");
for(var i = 0; i< window.localStorage.length; i++){
var key = window.localStorage.key(i);
var pp = JSON.parse(window.localStorage.getItem(key));
makeCircle(context, pp.x, pp.y, false);
}
}
/*事件注册*/
function canvasEventsInit() {
canvas.onmousedown = function (event) {
if(showTips == null){
dragging = true;
pos = windowToCanvas(event.clientX, event.clientY); //坐标转换,将窗口坐标转换成canvas的坐标
}
//console.log(pos);
};
canvas.onmousemove = function (evt) { //移动
if(showTips != null){
//测距功能
if(showTips){
showPosTips(originalPos.x, originalPos.y, evt);
}
}else{
if(dragging){
posl = windowToCanvas(evt.clientX, evt.clientY);
var x = posl.x - pos.x, y = posl.y - pos.y;
imgX += x;
imgY += y;
pos = JSON.parse(JSON.stringify(posl));
//document.body.style.cursor = 'Handle';
drawImage(); //重新绘制图片
}else{
pos3 = windowToCanvas(evt.clientX, evt.clientY);
//console.log("pos=" + pos3.x + "," + pos3.y + "; ");
if(context.isPointInPath(pos3.x, pos3.y)){
console.log("进入区域");
}
}
}
};
canvas.onmouseup = function (evt) {
if(showTips != null){
if(!showTips){
document.getElementById("mouseTip").style.display = 'block';
originalPos = {x: evt.clientX, y: evt.clientY};
showTips = true;
}else{
document.getElementById("mouseTip").style.display = 'none';
showTips = null;
}
}else{
dragging = false;
}
//document.body.style.cursor = 'default';
};
canvas.onmousewheel = canvas.onwheel = function (event) { //滚轮放大缩小
var pos = windowToCanvas (event.clientX, event.clientY);
event.wheelDelta = event.wheelDelta ? event.wheelDelta : (event.deltalY * (-40)); //获取当前鼠标的滚动情况
var newPos = {x:((pos.x-imgX)/imgScale).toFixed(2) , y:((pos.y-imgY)/imgScale).toFixed(2)};
//console.log("newPos==========" + newPos.x + "," + newPos.y)
if (event.wheelDelta > 0) {// 放大
imgScale +=0.1;
imgX = (1-imgScale)*newPos.x+(pos.x-newPos.x);
imgY = (1-imgScale)*newPos.y+(pos.y-newPos.y);
} else {// 缩小
imgScale -=0.1;
if(imgScale<MINIMUM_SCALE) {//最小缩放1
imgScale = MINIMUM_SCALE;
}
imgX = (1-imgScale)*newPos.x+(pos.x-newPos.x);
imgY = (1-imgScale)*newPos.y+(pos.y-newPos.y);
//console.log(imgX,imgY);
}
//console.log("wheel.imgY = " + imgY);
drawImage(); //重新绘制图片
};
canvas.ondblclick = function(event){
console.log("双击:鼠标坐标【" + (event.clientX + "," + event.clientY) + "】图片左上角坐标【" +imgX + "," +imgY + "】" +
"图片比例尺【" + scaleRate + "】 缩放比例【" +imgScale+"】分辨率【" +img.width + "X" + img.height +"】");
var p = windowToCanvas (event.clientX, event.clientY);
makeCircle(context, (Math.abs(imgX) + Math.abs(p.x))/imgScale, (Math.abs(imgY) + Math.abs(p.y))/imgScale, true);
console.log(" 距离画布边框【" +p.x + " " + p.y+"】");
//console.log((Math.abs(Math.abs(imgX) - event.clientX)) + "@@@" + (Math.abs(Math.abs(imgY) -event.clientY)));
}
}
function showPosTips(x, y, evt){
drawImage();
console.log(x +",,," + y);
//context.clearRect(0, 0, canvas.width, canvas.height);
context.beginPath();
context.stokeStyle="#f00";
context.moveTo(x, y);
var temp = windowToCanvas(evt.clientX, evt.clientY);
context.lineTo(temp.x, temp.y);
context.stroke();
document.getElementById("mouseTip").innerHTML = temp.x + "," + temp.y;
document.getElementById("mouseTip").style.left = evt.clientX + 'px';
document.getElementById("mouseTip").style.top = evt.clientY + 'px';
}
/*坐标转换*/
function windowToCanvas(x,y) {
var box = canvas.getBoundingClientRect();
//console.log( "x=" + (x - box.left - (box.width - canvas.width) / 2) + ",y=" + (y - box.top - (box.height - canvas.height) / 2));
//这个方法返回一个矩形对象,包含四个属性:left、top、right和bottom。分别表示元素各边与页面上边和左边的距离
return {
x: x - box.left - (box.width - canvas.width) / 2,
y: y - box.top - (box.height - canvas.height) / 2
};
}
function makeCircle(context, posX, posY, isCreate){
var resetX = posX/img.width*(img.width*imgScale) + imgX;
var resetY = posY/img.height * (img.height * imgScale) + imgY;
context.beginPath();//这里可以理解为另外起笔,如果忽略这个步骤那么下面的样式就会继承上面的,所以最好不要忽略
//context.fillStyle = 'red';//渐变填充样式
context.fillStyle = 'rgba(100,150,185,0.9)';//渐变填充样式
context.font = (imgScale>1?0.5:imgScale) * 44 + 'px STheiti, SimHei';
context.fillText('32', resetX-(20*(imgScale>1?0.5:imgScale)), resetY+(15*(imgScale>1?0.5:imgScale)));
context.strokeStyle = 'red';//定义填充样式
context.lineWidth = 2;//定义线性的线宽,宽是从圆圈向内外两边同时加粗的
//context.arc(519/img.width*(img.width*imgScale) + imgX,343/img.height * (img.height * imgScale) + imgY,50 * (imgScale>1?0.5:imgScale),0,2*Math.PI);//定义圆[这五个参数分别是(横坐标,纵坐标,半径,起始的点(弧度),结束的点(弧度))]
//context.arc(251.598/img.width*(img.width*imgScale) + imgX,1005.732/img.height * (img.height * imgScale) + imgY,50 * (imgScale>1?0.5:imgScale),0,2*Math.PI);//定义圆[这五个参数分别是(横坐标,纵坐标,半径,起始的点(弧度),结束的点(弧度))]
context.arc(resetX, resetY, 50 * (imgScale>1?0.5:imgScale),0,2*Math.PI);//定义圆[这五个参数分别是(横坐标,纵坐标,半径,起始的点(弧度),结束的点(弧度))]
//context.closePath();
//context.globalAlpha = 0.1;
//context.fill();//定义圆为面性即蓝色圆面
context.stroke();//只有加上这行才有边框
//context.restore();
if(isCreate){
window.localStorage.setItem(posX+"_" + posY, JSON.stringify({x: posX, y: posY}));
}
}
function jianbian(){
var gradient=context.createLinearGradient(0,0,20,0);
gradient.addColorStop("0","magenta");
gradient.addColorStop("0.5","blue");
gradient.addColorStop("1.0","red");
return gradient;
}
</script>
</body>
</html>
其中“F:/image.jpg”换成自己本地的图片就行了