组件代码:
<template>
<div style="width:100%;height:100%;display:flex;flex-direction:column;">
<slot name="title"></slot>
<div style="width:100%;height:100%;position:relative;" :id="'canvas_box' + id">
<canvas :id="'canvas' + id" @mousemove="onMouseMove">你的浏览器不支持canvas,请跟换其他浏览器试一试</canvas>
<div class="mess_box" id="mess_box" v-show="messActive" :style="{left:(mouseLeft +'px'),top:(mouseHight +'px')}">
<li style="text-align:left;" v-for="(item,index) in messData">{{item}}</li>
</div>
</div>
</div>
</template>
<script>
// https://www.npmjs.com/package/element-resize-detector#removelistenerelement-listener
let elementResizeDetectorMaker = require("element-resize-detector")
export default {
props: {
// 图片地址
imgUrl:{
type:String,
detault:() => ('')
},
// 展示测点
dotList:{
type:Array,
detault:() => ([])
},
// 公共配置
option: {
type: Object,
default: () => ({
// 信息栏
tooltip:{
style:{
marginLeft:20,
marginRight:20,
marginTop:20,
marginBottom:20,
fontSize:12,
color:"#fff",
background:"rgba(0,0,0,0.5)"
}
}
})
},
formatter:{
type:Function,
detault:() => ({})
}
},
data(){
return {
erd:null,
id:'#' + Math.random(),
canvasWidth:null,
canvasHeight:null,
context:null,
isDraw:false,
dotArr:[],
imgMess:null,
canvas_element:null,
canva_box_element:null,
// 保存画布上所有的圆圈
circles:[],
//当前选中的圆圈
previousSelectedCircle:null,
messActive:false,
messData:null,//div 信息框
mouseLeft:0,
mouseHight:0,
}
},
watch:{
dotList:{
handler(val){
console.log(this.dotList)
let arr = [];
for(let i = 0; i < this.dotList.length; i++){
var { ...obj2 } = this.dotList[i];
arr.push({
data:obj2,
x:'',
y:'',
radius:'',
isSelected:false,
percentageX:this.dotList[i].XAxis,
percentageY:this.dotList[i].YAxis,
PlotColor:this.dotList[i].PlotColor
})
}
this.dotArr = arr;
let that = this;
that.drawImage();
setTimeout(()=>{
that.RepaintPoint();
},300)
},
deep:true
},
imgUrl:{
handler(val){
let arr = [];
for(let i = 0; i < this.dotList.length; i++){
var { ...obj2 } = this.dotList[i];
arr.push({
data:obj2,
x:'',
y:'',
isSelected:false,
percentageX:this.dotList[i].XAxis,
percentageY:this.dotList[i].YAxis,
PlotColor:this.dotList[i].PlotColor
})
}
this.dotArr = arr;
let that = this;
that.drawImage();
setTimeout(()=>{
that.RepaintPoint();
},300)
},
deep:true
},
},
methods: {
// 初始化
initChart(){
this.canvas_element.height = this.canva_box_element.offsetHeight;
this.canvas_element.width = this.canva_box_element.offsetWidth;
this.canvasWidth = this.canva_box_element.offsetWidth;
this.canvasHeight = this.canva_box_element.offsetHeight;
this.drawImage().then((res)=>{
if(res == 'success'){
console.log("333")
this.RepaintPoint();
}
});
},
// div信息框
drawToolTip(txt, x, y) {
this.messData = txt;
if((x + document.getElementById("mess_box").offsetWidth) > this.canvasWidth){
this.mouseLeft = this.canvasWidth - document.getElementById("mess_box").offsetWidth;
this.mouseHight = y;
}else if((y + document.getElementById("mess_box").offsetHeight) > this.canvasHeight){
this.mouseLeft = x;
this.mouseHight = this.canvasHeight - document.getElementById("mess_box").offsetHeight;
}else{
this.mouseLeft = x;
this.mouseHight = y;
}
this.messActive = true;
},
//鼠标移动事件
onMouseMove(e) {
// 清除之前选择的圆圈
if (this.previousSelectedCircle != null) {
this.previousSelectedCircle.isSelected = false;
this.previousSelectedCircle = null;
}
let canvas_x = this.canvas_element.getBoundingClientRect().left;
let canvas_y = this.canvas_element.getBoundingClientRect().top;
// 取得画布上被单击的点
let clickX = e.clientX - canvas_x;
let clickY = e.clientY - canvas_y;
// 查找被单击的圆圈
for(let i=this.dotArr.length-1; i>=0; i--) {
let circle = this.dotArr[i];
//使用勾股定理计算这个点与圆心之间的距离
let distanceFromCenter = Math.sqrt(Math.pow(circle.x - clickX, 2)
+ Math.pow(circle.y - clickY, 2))
// 判断这个点是否在圆圈中
if (distanceFromCenter <= circle.radius) {
this.previousSelectedCircle = circle;
//选择新圆圈
circle.isSelected = true;
//停止搜索
break;
}
}
// //更新显示,重绘圆圈
this.drawImage().then((res)=>{
if(res == 'success'){
this.RepaintPoint();
// //如果当前鼠标位置有圆圈,还要显示tip
if(this.previousSelectedCircle != null){
this.formatter(this.previousSelectedCircle).then(res=>{
this.drawToolTip(res, clickX, clickY);
})
}else{
this.messActive = false;
}
}
});
},
// 重绘点
RepaintPoint(point){
let context = this.canvas_element.getContext('2d');
if(!this.dotList){
return
}
let arr = [];
for(let i = 0; i < this.dotList.length; i++){
var { ...obj2 } = this.dotList[i];
arr.push({
data:obj2,
x:'',
y:'',
radius:'',
isSelected:false,
percentageX:this.dotList[i].XAxis,
percentageY:this.dotList[i].YAxis,
PlotColor:this.dotList[i].PlotColor
})
}
this.dotArr = arr;
for(let i = 0; i < this.dotArr.length; i++){
if (this.dotArr[i].isSelected) {
context.lineWidth = 5;
}else {
context.lineWidth = 1;
}
this.dotArr[i].x = this.imgMess.dx + this.imgMess.dWidth * this.dotArr[i].percentageX;
this.dotArr[i].y = this.imgMess.dy + this.imgMess.dHeight * this.dotArr[i].percentageY;
this.dotArr[i].radius = 3;
context.fillStyle=this.dotArr[i].PlotColor;
context.beginPath();
context.arc(this.imgMess.dx + this.imgMess.dWidth * this.dotArr[i].percentageX,this.imgMess.dy + this.imgMess.dHeight * this.dotArr[i].percentageY,3,0,Math.PI*2,true);
context.closePath();
context.fill();
}
},
drawImage(){
return new Promise((resolve, reject) => {
let context = this.canvas_element.getContext('2d');
context.fillStyle = '#fff';
// 新建一个 canvas 作为缓存 canvas
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
tempCanvas.width = this.canvasWidth; tempCanvas.height = this.canvasHeight; // 设置宽高
// 开始绘制
let img = new Image();
img.onload = () => {
let imgRect = this.containImg(0, 0, this.canvasWidth, this.canvasHeight,img.width,img.height);
tempCtx.drawImage(img, imgRect.dx, imgRect.dy, imgRect.dWidth, imgRect.dHeight);
// 清空
context.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
context.drawImage(tempCanvas,0,0);
resolve("success")
}
img.src = this.imgUrl;
//注:img预加载模式下,onload应该放在为src赋值的上面,以避免已有缓存的情况下无法触发onload事件从而导致onload中的事件不执行的情况发生
});
},
/**
* @param {Number} sx 固定盒子的x坐标,sy 固定盒子的y左标
* @param {Number} box_w 固定盒子的宽, box_h 固定盒子的高
* @param {Number} source_w 原图片的宽, source_h 原图片的高
* @return {Object} {drawImage的参数,缩放后图片的x坐标,y坐标,宽和高},对应drawImage(imageResource, dx, dy, dWidth, dHeight)
*/
// 等比缩放绘制图片
containImg(sx, sy , box_w, box_h, source_w, source_h){
let dx = sx,
dy = sy,
dWidth = box_w,
dHeight = box_h;
// 分别计算图片相对于画布的宽高缩放比,采用最大的缩放值
let scaleX = box_w/source_w;
let scaleY = box_h/source_h;
let scaleNum;
if(scaleX <= scaleY){
scaleNum = scaleX
}else{
scaleNum = scaleY
}
dWidth = source_w * scaleNum;
dHeight = source_h * scaleNum;
dx = 0.5 * (box_w - dWidth) + dx;
dy = 0.5 * (box_h - dHeight) + dy;
this.imgMess = {
dx,
dy,
dWidth,
dHeight
}
return{
dx,
dy,
dWidth,
dHeight
}
},
onWindowResize(){
this.erd = elementResizeDetectorMaker({
strategy: "scroll",
callOnAdd: true,
debug: false
})
let that = this;
this.erd.listenTo(this.canva_box_element, function (element) {
let width = element.offsetWidth
let height = element.offsetHeight
that.$nextTick(function () {
that.canvas_element.height = height;
that.canvas_element.width = width;
that.canvasWidth = width;
that.canvasHeight = height;
that.drawImage().then((res)=>{
if(res == 'success'){
that.RepaintPoint();
}
});
})
})
}
},
mounted () {
this.$nextTick(() => {
this.canvas_element=document.getElementById('canvas'+ this.id);
this.canva_box_element = document.getElementById("canvas_box"+ this.id);
this.initChart();
this.onWindowResize();
})
},
created(){
},
beforeDestroy() {
this.erd.removeAllListeners(this.canva_box_element)
}
}
</script>
<style scoped>
.mess_box{
position: absolute;
border-style: solid;
white-space: nowrap;
transition: left 0.4s cubic-bezier(0.23, 1, 0.32, 1) 0s,
top 0.4s cubic-bezier(0.23, 1, 0.32, 1) 0s;
background-color: rgba(50, 50, 50, 0.7);
border-width: 0px;
border-color: rgb(51, 51, 51);
border-radius: 4px;
color: rgb(255, 255, 255);
font: 14px/21px "Microsoft YaHei";
padding: 5px;
pointer-events: none;
}
</style>
父组件:
<picture-tracing-show-point :formatter="formatter" :dotList="unitDotsArr" :imgUrl="unitImg"></picture-tracing-show-point>
这个是用来把信息框要展示的数据抛出道父盒子进行方便设置
formatter: function(datas) {
let str = ['截面名称:' + datas.data.SectionName,'编 号:'+ datas.data.SectionSerialNum,'位置描述:' + datas.data.SectionMemo]
return new Promise((resolve, reject) => {
resolve(str)
})
},
unitDotsArr描点数据就是按照百分比进行设置的