技術要點:
1.img 繪制到canvas
2.繪制完成以后進行拖拽,縮放
3.使用canvas畫圖,在繪制的img上進行標記划線,當然可以實現跟過功能,例如百度地圖的功能,做單個標記,區域標記等。
4.實現坐標等轉換,標記區域的所有坐標都是基於相對原始圖片的坐標,便於其他操作。
實際項目中的開發實現效果截圖如下:
點擊邊界標記,就可以開始左鍵划線功能,會自動形成閉合區域,點擊右鍵結束划線。同時可以刪除當前繪制的區域。
區域標記完成以后,就可以進行設備的選擇,設備從左側列表點擊以后,放到右側canvas 的區域,放下后還可以繼續拖拽改變其位置,而且保持對應關系。
這些標記區域和標記點都可以基於底圖的縮放和拖拽進行位置的等比例渲染,但是保存的坐標始終是基於原圖的。
部分效果源碼,本地看的話需要給img 圖片路徑,正確的路徑。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>標記</title>
</head>
<style>
html, body {
height: 100%;
min-height: 100%;
overflow: hidden;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
border:none;
}
.canvasWrap {
width: 100%;
height: 100%;
background: #ccc;
}
.mark_list{
position: absolute;
top: 20px;
right: 10px;
}
.mark_list li{
float: left;
width: 100px;
border-radius: 4px;
border: 1px solid #ccc;
list-style: none;
line-height: 30px;
text-align: center;
color:#333;
background: #fff;
cursor: pointer;
}
.mark_list li:hover{
background: #009a8f;
color:#fff;
}
</style>
<body>
<div class="canvasWrap" id="wrap">
<canvas id="draw">
</canvas>
<ul class="mark_list">
<li class="border_mark">標記區域</li>
<li>標記點位</li>
</ul>
</div>
</body>
<script>
function MarkPoints(Imgurl) {
this.imgX = 0;//在畫布上圖片的X偏移量
this.imgY = 0;//在畫布上圖片的Y偏移量
this.imgScale = 1;//圖片的縮放比例
this.rateNum;//圖片高度自適應比例,圖片等比居中展示在canvas
this.scaleFlag = 0;//縮放因子,最大縮放9,最小縮放-9
this.context;
this.img;
this.pos={};//每次拖拽前坐標保存
this.dragFlag=false;//是否可拖拽當前img,默認不能
this.markFlag=false;//標記區域開啟關閉flag
this.CreatLinepoints = [];//每次創建新區域的坐標集合
this.allMarkLins = [];//已創建的區域集合,例如[[{x,y},{x,y},{x,y}],[{n,m},{n,m},{n,m}]]目前只需要一個區域,所以數組內部只有一項
this.getImgLoad(Imgurl);
this.init();
document.oncontextmenu = new Function("event.returnValue=false;");
document.onselectstart = new Function("event.returnValue=false;");
}
MarkPoints.prototype = {
getImgLoad: function (Imgurl) {
var _this = this;
var wrap = document.getElementById('wrap');
_this.canvas = document.getElementById('draw');
_this.context = draw.getContext('2d');
_this.canvas.height = wrap.offsetHeight;
_this.canvas.width = wrap.offsetWidth;
_this.img = new Image();
_this.img.onload = function () {
_this.imgX = 0;
_this.imgY = 0;
_this.imgScale = 1;
_this.imgScale=_this.rateNum = _this.canvas.height / _this.img.naturalHeight;
_this.imgX = (_this.canvas.width - _this.img.naturalWidth * _this.imgScale * _this.rateNum) / 2;//默認進來當前圖像劇中顯示
/*畫出當前圖片*/
_this.drawImg();
}
_this.img.src = imgUrl;
},
getNewPoints: function (points) {
var _this=this;
var newPointAry = [];
for (var i = 0; i < points.length; i++) {
var obj = {};
obj.x = points[i].x * _this.imgScale + _this.imgX;
obj.y = points[i].y * _this.imgScale + _this.imgY;
if (points[i].hasOwnProperty('mac')) {
obj.mac = points[i].mac;
obj.name = points[i].name || '';
}
newPointAry.push(obj);
}
return newPointAry;
},
drawImg: function () {
var _this = this;
_this.context.clearRect(0, 0, _this.canvas.width, _this.canvas.height);
_this.context.drawImage(_this.img, 0, 0, _this.img.naturalWidth, _this.img.naturalHeight, _this.imgX, _this.imgY, _this.img.naturalWidth * _this.imgScale * _this.rateNum, _this.img.naturalHeight * _this.imgScale * _this.rateNum);
if ( _this.allMarkLins.length) {
for (var m = 0; m < _this.allMarkLins.length; m++) {
var points = _this.allMarkLins[m];
var newPoints = _this.getNewPoints(points);
for (var i = 0; i < newPoints.length; i++) {
var can = _this.context;
can.beginPath();
can.arc(newPoints[i].x, newPoints[i].y, 6, 0, Math.PI * 2, true);
can.fillStyle = "#FF423E";
can.fill();
can.strokeStyle = "#FFF";
can.stroke();//畫空心圓
can.closePath();
if (points.length >= 2 && i >= 1) {
can.strokeStyle = "#FF423E";
can.lineWidth = 2;
can.beginPath();
can.moveTo(newPoints[i - 1].x, newPoints[i - 1].y);
can.lineTo(newPoints[i].x, newPoints[i].y);
can.fillStyle = "#ff0000";
can.fill();
can.stroke();
can.closePath();
}
}
if (points.length >= 3) {
can.strokeStyle = "#FF423E";
can.lineWidth = 2;
can.beginPath();
can.moveTo(newPoints[newPoints.length - 1].x, newPoints[newPoints.length - 1].y);
can.lineTo(newPoints[0].x, newPoints[0].y);
can.stroke();
can.closePath();
}
}
;
}
},
init:function () {
var _this=this;
_this.canvas.onmousedown=function(event){
_this.clickDown(event);
};
_this.canvas.onmousemove=function(event){
_this.mouseMove(event)
};
_this.canvas.onmouseup=function (event) {
_this.mouseUp(event);
}
_this.canvas.onmousewheel=function (event) {
_this.onmouseWheel(event);
}
document.getElementsByClassName('border_mark')[0].onclick=function () {
_this.MarkBorderline();
}
},
/*計算當前鼠標位置距離canvas的偏移量*/
xyToCanvas:function(ele,x,y){
var _this=this;
var obj = _this.canvas.getBoundingClientRect();
return {
x: x - obj.left,
y: y - obj.top
};
},
/*鼠標單擊事件*/
clickDown:function(event){
var _this=this;
if (_this.markFlag) {
_this.canvas.style.cursor = "none";
if (event.button == 2) {
_this.markFlag = false;
_this.dragFlag = true;
_this.canvas.style.cursor = "normal";
_this.drawImg();
return;
} else {
var posXY = _this.xyToCanvas(_this.canvas, event.clientX, event.clientY);
posXY.x = (posXY.x - _this.imgX) / _this.imgScale;
posXY.y = (posXY.y - _this.imgY) / _this.imgScale;
_this.CreatLinepoints.push(posXY);
_this.allMarkLins.pop();
_this.allMarkLins.push(_this.CreatLinepoints);
_this.drawImg();
}
return;
}
if ( event.button == 0) {//點擊鼠標左鍵
_this.dragFlag = true;
_this.canvas.style.cursor = "move";
_this.pos = _this.xyToCanvas(_this.canvas, event.clientX, event.clientY);
}
},
/*鼠標移動事件*/
mouseMove:function(event){
var _this=this;
/*拖拽*/
if (_this.dragFlag) {
_this.canvas.style.cursor = "move";
var pos1 = _this.xyToCanvas( _this.canvas, event.clientX, event.clientY);
var x = pos1.x - _this.pos.x;
var y = pos1.y - _this.pos.y;
_this.pos = pos1;
_this.imgX += x;
_this.imgY += y;
_this.drawImg();
}
/*邊界標記*/
if (!_this.dragFlag&& _this.markFlag) {
var pos1 = _this.xyToCanvas(_this.canvas, event.clientX, event.clientY);
var can = _this.context;
can.clearRect(0, 0, _this.canvas.width, _this.canvas.height);
_this.drawImg();
/*畫跟隨圓點*/
can.beginPath();
// can.fillText('[' + point.x + ', ' + point.y + ']', 15, 25 * (points.length + 1))
can.arc(pos1.x, pos1.y, 6, 0, Math.PI * 2, true);
can.fillStyle = "#FF423E";
can.fill();
can.strokeStyle = "#FFF";
can.stroke();//畫空心圓
can.closePath();
/*當前的坐標未結束那么繼續 跟隨直線*/
if (!_this.CreatLinepoints.length) return;
can.strokeStyle = "red";
can.beginPath();
can.moveTo(_this.CreatLinepoints[_this.CreatLinepoints.length - 1].x * _this.imgScale + _this.imgX, _this.CreatLinepoints[_this.CreatLinepoints.length - 1].y * _this.imgScale + _this.imgY);
can.lineTo(pos1.x, pos1.y);
can.stroke();
can.closePath();
}
},
/*鼠標放開事件*/
mouseUp:function (event) {
var _this=this;
_this.dragFlag=false;
if (_this.markFlag) {
_this.canvas.style.cursor = "none";
return;
}
_this.canvas.style.cursor='default';
},
/*鼠標滾輪事件*/
onmouseWheel:function(event){
var _this=this;
var pos =_this.xyToCanvas(_this.canvas, event.clientX, event.clientY);
event.wheelDelta = event.wheelDelta ? event.wheelDelta : (event.deltaY * (-40));
if (event.wheelDelta > 0 && _this.scaleFlag < 9) {
_this.imgScale *= 2;
_this.imgX = _this.imgX * 2 - pos.x;
_this.imgY = _this.imgY * 2 - pos.y;
_this.scaleFlag += 1;
}
if (event.wheelDelta < 0 && _this.scaleFlag > -9) {//縮小
_this.imgScale *= 0.5;
_this.imgX = _this.imgX * 0.5 + pos.x * 0.5;
_this.imgY = _this.imgY * 0.5 + pos.y * 0.5;
_this.scaleFlag -= 1;
}
_this.drawImg();
},
/*邊界標記*/
MarkBorderline: function () {
var _this=this;
_this.markFlag = true;//切換為true,禁止拖拽,只能標記
_this.canvas.style.cursor = "none";
_this.CreatLinepoints = [];
_this.allMarkLins.push([]);
},
/*刪除標記區域*/
deleteArea: function (id) {
var _this = this;
_this.allMarkLins.splice(id, 1);
_this.drawImg();
},
}
var imgUrl = 'img/girl.jpg';//圖片路徑
new MarkPoints(imgUrl);
</script>
</html>