需求:使用cavans和div实现,当鼠标滑入指定区域时显示当前鼠标指定的位置,并且在指定区域内可自由框选区域,并且框选的区域可以拖拽
实现效果(不会放视频,就只截了两张图意思一下)
鼠标按住选中区可滑动
JS版本
class createCanvas {
constructor(options) {
options = Object.assign(
{
container: document.body, // 父容器DOM, 默认是body
lineColor: "black", // canvas中画的线的颜色
regionBg: "pink", // 框选区域的背景颜色
regionCanMove: true, // 框选区是否可拖动
showLine: true, // 是否显示canvas中的线
cursor: "crosshair", // canvas中鼠标的形状
showValTip: true, // 是否显示数据提示,如果设置为true,则需要调用实例的pointVal方法传值
valTipStyle: {
// 数据提示的样式
fontSize: "12px",
color: "pink",
backgroundColor: "black",
padding: "4px",
offset: 2, // 提示框距离线的偏移量
},
},
options
);
this.tipVal = "";
this.options = options;
let { container } = options;
this.container = container || document.body;
// 计算父级元素的宽高帮保存到实例上
this.H = this.container.clientHeight;
this.W = this.container.clientWidth;
// 初始化 canvas
this.initCanvas(this.W, this.H);
}
// 创建canvas画布
initCanvas(w, h) {
w = w || this.W;
h = h || this.H;
let canvas = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
canvas.style.position = "absolute";
canvas.style.cursor = this.options.cursor;
canvas.style.zIndex = 2;
this.canvas = canvas;
// 插入canvasDOM
this.container.appendChild(canvas);
this.ctx = this.canvas.getContext("2d");
console.dir(this.ctx);
this.canvas.addEventListener("mouseenter", this.canvasEnter);
this.canvas.addEventListener("mousedown", this.canvasDown);
}
// canvas进入时开始画线,并创建tipDOM
canvasEnter = (ev) => {
let valTip = document.createElement("span"),
offset = this.options.valTipStyle.offset || 5;
if (this.options.showValTip) {
valTip.style.position = "absolute";
valTip.style.whiteSpace = "nowrap";
let { fontSize, color, backgroundColor, padding } =
this.options.valTipStyle;
valTip.style.fontSize = fontSize || "12px";
valTip.style.color = color || "black";
valTip.style.padding = padding || "4px";
valTip.style.backgroundColor = backgroundColor || "pink";
valTip.style.borderRadius = "2px";
valTip.innerHTML = this.tipVal || "提示框";
this.canvas.valTip = valTip;
this.container.appendChild(this.canvas.valTip);
} else {
valTip = null;
}
let ctx = this.ctx;
let W = this.canvas.width,
H = this.canvas.height,
lineColor = this.options.lineColor;
// 鼠标进入canvas时画线
const canvasMove = (ev) => {
if (this.options.showLine) {
// 每次画之前先清除之前的内容
ctx.clearRect(0, 0, W, H);
ctx.setLineDash([4, 2]); //线的样式
// 画 Y 线
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = lineColor; // 线的颜色
ctx.moveTo(0, ev.offsetY);
ctx.lineTo(this.W, ev.offsetY);
// 画 X 线
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = lineColor;
ctx.moveTo(ev.offsetX, 0);
ctx.lineTo(ev.offsetX, H);
ctx.lineWidth = 1;
}
// 计算 valTip 的宽高,
if (this.options.showValTip) {
let valW = valTip.offsetWidth,
valH = valTip.offsetHeight;
this.canvas.valTip.style.top = ev.offsetY - 20 - offset + "px";
this.canvas.valTip.style.left = ev.offsetX + offset + "px";
if (ev.offsetY - this.canvas.offsetTop <= valH + offset) {
this.canvas.valTip.style.top = ev.offsetY + offset + "px";
}
if (this.canvas.offsetWidth - ev.offsetX <= valW + offset) {
this.canvas.valTip.style.left = ev.offsetX - offset - valW + "px";
}
}
};
// 鼠标移出canvas
const canvasOut = (ev) => {
ctx.clearRect(0, 0, W, H);
this.options.showValTip ? this.canvas.valTip.remove() : null;
};
this.canvas.addEventListener("mousemove", canvasMove);
this.canvas.addEventListener("mouseout", canvasOut);
};
// canvas鼠标按下的时候创建 regionDOM,并记录鼠标位置作为region的left
canvasDown = (ev) => {
ev.stopPropagation();
if (this.canvas.region) {
this.container.removeChild(this.canvas.region);
delete this.canvas.region;
}
let region = document.createElement("div");
region.style.position = "absolute";
region.style.cursor = "move";
region.className = "region";
region.startX = ev.offsetX;
region.endX = 0;
region.style.width = "0";
region.style.zIndex = 1;
this.canvas.region = region;
this.container.appendChild(this.canvas.region);
this.canvas.addEventListener("mousemove", this.regionChange);
let that = this;
// canvas内鼠标抬起
this.canvas.addEventListener("mouseup", (ev) => {
that.canvas.removeEventListener("mousemove", that.regionChange);
that.canvas.region.endX = ev.offsetX;
that.canvas.region.style.zIndex = 1;
if (that.options.regionCanMove) {
that.canvas.region.style.zIndex = 3;
that.canvas.region.addEventListener("mousedown", that.regionDown);
that.canvas.region.addEventListener("mouseup", () => {
that.canvas.region.removeEventListener("mousemove", that.regionMove);
});
}
});
};
// 框选开始
regionChange = (ev) => {
let { borderColor, opacity, backgroundColor } = this.options.regionStyle;
let canvas = this.canvas,
region = canvas.region,
H = canvas.height;
region.endX = ev.offsetX + canvas.offsetLeft;
region.style.backgroundColor = backgroundColor || "pink";
region.style.opacity = opacity || 0.5;
region.style.borderLeft =
region.style.borderRight = `0.5px solid ${borderColor}`;
region.style.top = 0 + "px";
region.style.height = H + "px";
if (region.endX > region.startX) {
// 从左往右
region.style.left = region.startX + "px";
region.style.width = region.endX - region.startX + "px";
} else {
// 从右往左
region.style.right = region.startX + "px";
region.style.width = region.startX - region.endX + "px";
region.style.left = region.endX + "px";
}
};
// region开始拖动
regionDown = (ev) => {
ev.stopPropagation();
let region = this.canvas.region,
left = region.offsetLeft,
cleft = ev.clientX - left;
region.cleft = cleft;
if (this.options.regionCanMove) {
region.addEventListener("mousemove", this.regionMove);
region.addEventListener("mouseout", () => {
this.canvas.region.removeEventListener("mousemove", this.regionMove);
});
}
};
// region拖动中
regionMove = (ev) => {
let region = this.canvas.region,
moveLeft = ev.clientX - region.cleft;
// 判断可移动的区域
if (moveLeft <= 0) {
region.style.left = this.canvas.offsetLeft + "px";
} else if (moveLeft + region.clientWidth > this.canvas.width) {
region.style.left = this.canvas.width - region.clientWidth + "px";
} else {
region.style.left = moveLeft + "px";
}
};
createRegion(left, width) {
if (this.canvas.region) {
this.container.removeChild(this.canvas.region);
delete this.canvas.region;
}
let region = document.createElement("div");
region.style.position = "absolute";
region.style.cursor = "move";
region.className = "region";
region.startX = left;
region.endX = width;
region.style.left = left + "px";
region.style.width = width + "px";
region.style.height = this.canvas.height + "px";
let { borderColor, opacity, backgroundColor } = this.options.regionStyle;
region.style.backgroundColor = backgroundColor || "pink";
region.style.opacity = opacity || 0.5;
region.style.borderLeft =
region.style.borderRight = `0.5px solid ${borderColor}`;
region.style.zIndex = 1;
this.canvas.region = region;
this.container.appendChild(this.canvas.region);
if (this.options.regionCanMove) {
region.style.zIndex = 3;
this.canvas.region.addEventListener("mousedown", this.regionDown);
this.canvas.region.addEventListener("mouseup", () => {
this.canvas.region.removeEventListener("mousemove", this.regionMove);
});
}
}
pointVal(val) {
this.tipVal = val;
}
}
window.createCanvas = createCanvas;
TypeScript版
interface options {
container: HTMLElement; // 父容器DOM, 默认是body
lineColor: string; // canvas中画的线的颜色
regionBg: string; // 框选区域的背景颜色
regionCanMove: boolean; // 框选区是否可拖动
showLine: boolean; // 是否显示canvas中的线
cursor: string; // canvas中鼠标的形状
showValTip: boolean; // 是否显示数据提示,如果设置为true,则需要调用实例的pointVal方法传值
valTipStyle: {
// 数据提示的样式
fontSize: string;
color: string;
backgroundColor: string;
padding: string;
offset: number; // 提示框距离线的偏移量
};
regionStyle: {
// 框选区的样式
borderColor: string;
opacity: number;
backgroundColor: string;
};
}
class createCanvas {
options: options;
tipVal: string;
container: HTMLElement;
H: number;
W: number;
canvas: any;
ctx: CanvasRenderingContext2D;
constructor(options) {
options = Object.assign(
{
container: document.body, // 父容器DOM, 默认是body
lineColor: "black", // canvas中画的线的颜色
regionBg: "pink", // 框选区域的背景颜色
regionCanMove: true, // 框选区是否可拖动
showLine: true, // 是否显示canvas中的线
cursor: "crosshair", // canvas中鼠标的形状
showValTip: true, // 是否显示数据提示,如果设置为true,则需要调用实例的pointVal方法传值
valTipStyle: {
// 数据提示的样式
fontSize: "12px",
color: "pink",
backgroundColor: "black",
padding: "4px",
offset: 2, // 提示框距离线的偏移量
},
regionStyle: {
borderColor: "#49A4FF",
opacity: 0.2,
backgroundColor: "#fff",
},
},
options
);
this.tipVal = "";
this.options = options;
let { container } = options;
this.container = container || document.body;
// 计算父级元素的宽高并保存到实例上以便后期调用
this.H = this.container.clientHeight;
this.W = this.container.clientWidth;
// 初始化 canvas
this.initCanvas(this.W, this.H);
}
// 创建canvas画布
initCanvas(w, h) {
w = w || this.W;
h = h || this.H;
let canvas: HTMLCanvasElement = document.createElement("canvas");
canvas.width = w;
canvas.height = h;
canvas.style.position = "absolute";
canvas.style.cursor = this.options.cursor;
canvas.style.zIndex = "2";
this.canvas = canvas;
// 插入canvasDOM
this.container.appendChild(canvas);
this.ctx = this.canvas.getContext("2d");
this.canvas.addEventListener("mouseenter", this.canvasEnter);
this.canvas.addEventListener("mousedown", this.canvasDown);
}
// canvas进入时开始画线,并创建tipDOM
canvasEnter = (ev) => {
let valTip = document.createElement("span"),
offset = this.options.valTipStyle.offset || 5;
if (this.options.showValTip) {
valTip.style.position = "absolute";
valTip.style.whiteSpace = "nowrap";
let { fontSize, color, backgroundColor, padding } =
this.options.valTipStyle;
valTip.style.fontSize = fontSize || "12px";
valTip.style.color = color || "black";
valTip.style.padding = padding || "4px";
valTip.style.backgroundColor = backgroundColor || "pink";
valTip.style.borderRadius = "2px";
valTip.innerHTML = this.tipVal || "提示框";
this.canvas.valTip = valTip;
this.container.appendChild(this.canvas.valTip);
} else {
valTip = null;
}
let ctx = this.ctx;
let W = this.canvas.width,
H = this.canvas.height,
lineColor = this.options.lineColor;
// 鼠标进入canvas时画线
const canvasMove = (ev) => {
if (this.options.showLine) {
// 每次画之前先清除之前的内容
ctx.clearRect(0, 0, W, H);
ctx.setLineDash([4, 2]); //线的样式
// 画 Y 线
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = lineColor; // 线的颜色
ctx.moveTo(0, ev.offsetY);
ctx.lineTo(this.W, ev.offsetY);
// 画 X 线
ctx.stroke();
ctx.beginPath();
ctx.strokeStyle = lineColor;
ctx.moveTo(ev.offsetX, 0);
ctx.lineTo(ev.offsetX, H);
ctx.lineWidth = 1;
}
// 计算 valTip 的宽高,
if (this.options.showValTip) {
let valW = valTip.offsetWidth,
valH = valTip.offsetHeight;
this.canvas.valTip.style.top = ev.offsetY - 20 - offset + "px";
this.canvas.valTip.style.left = ev.offsetX + offset + "px";
if (ev.offsetY - this.canvas.offsetTop <= valH + offset) {
this.canvas.valTip.style.top = ev.offsetY + offset + "px";
}
if (this.canvas.offsetWidth - ev.offsetX <= valW + offset) {
this.canvas.valTip.style.left = ev.offsetX - offset - valW + "px";
}
}
};
// 鼠标移出canvas
const canvasOut = (ev) => {
ctx.clearRect(0, 0, W, H);
this.options.showValTip ? this.canvas.valTip.remove() : null;
};
this.canvas.addEventListener("mousemove", canvasMove);
this.canvas.addEventListener("mouseout", canvasOut);
};
// canvas鼠标按下的时候创建 regionDOM,并记录鼠标位置作为region的left
canvasDown = (ev) => {
ev.stopPropagation();
if (this.canvas.region) {
this.container.removeChild(this.canvas.region);
delete this.canvas.region;
}
let region: any = document.createElement("div");
region.style.position = "absolute";
region.style.cursor = "move";
region.className = "region";
region.startX = ev.offsetX;
region.endX = 0;
region.style.width = "0";
region.style.zIndex = "1";
this.canvas.region = region;
this.container.appendChild(this.canvas.region);
this.canvas.addEventListener("mousemove", this.regionChange);
let that = this;
// canvas内鼠标抬起
this.canvas.addEventListener("mouseup", (ev) => {
that.canvas.removeEventListener("mousemove", that.regionChange);
that.canvas.region.endX = ev.offsetX;
that.canvas.region.style.zIndex = "1";
if (that.options.regionCanMove) {
that.canvas.region.style.zIndex = "3";
that.canvas.region.addEventListener("mousedown", that.regionDown);
that.canvas.region.addEventListener("mouseup", () => {
that.canvas.region.removeEventListener("mousemove", that.regionMove);
});
}
});
};
// 框选开始
regionChange = (ev) => {
let { borderColor, opacity, backgroundColor } = this.options.regionStyle;
let canvas = this.canvas,
region = canvas.region,
H = canvas.height;
region.endX = ev.offsetX + canvas.offsetLeft;
region.style.backgroundColor = backgroundColor || "pink";
region.style.opacity = opacity || 0.5;
region.style.borderLeft =
region.style.borderRight = `0.5px solid ${borderColor}`;
region.style.top = 0 + "px";
region.style.height = H + "px";
if (region.endX > region.startX) {
// 从左往右
region.style.left = region.startX + "px";
region.style.width = region.endX - region.startX + "px";
} else {
// 从右往左
region.style.right = region.startX + "px";
region.style.width = region.startX - region.endX + "px";
region.style.left = region.endX + "px";
}
};
// region开始拖动
regionDown = (ev) => {
ev.stopPropagation();
let region = this.canvas.region,
left = region.offsetLeft,
cleft = ev.clientX - left;
region.cleft = cleft;
if (this.options.regionCanMove) {
region.addEventListener("mousemove", this.regionMove);
region.addEventListener("mouseout", () => {
this.canvas.region.removeEventListener("mousemove", this.regionMove);
});
}
};
// region拖动中
regionMove = (ev) => {
let region = this.canvas.region,
moveLeft = ev.clientX - region.cleft;
// 判断可移动的区域
if (moveLeft <= 0) {
region.style.left = this.canvas.offsetLeft + "px";
} else if (moveLeft + region.clientWidth > this.canvas.width) {
region.style.left = this.canvas.width - region.clientWidth + "px";
} else {
region.style.left = moveLeft + "px";
}
};
createRegion(left, width) {
if (this.canvas.region) {
this.container.removeChild(this.canvas.region);
delete this.canvas.region;
}
let region: any = document.createElement("div");
region.style.position = "absolute";
region.style.cursor = "move";
region.className = "region";
region.startX = left;
region.endX = width;
region.style.left = left + "px";
region.style.width = width + "px";
region.style.height = this.canvas.height + "px";
let { borderColor, opacity, backgroundColor } = this.options.regionStyle;
region.style.backgroundColor = backgroundColor || "pink";
region.style.opacity = opacity || 0.5;
region.style.borderLeft =
region.style.borderRight = `0.5px solid ${borderColor}`;
region.style.zIndex = 1;
this.canvas.region = region;
this.container.appendChild(this.canvas.region);
if (this.options.regionCanMove) {
region.style.zIndex = 3;
this.canvas.region.addEventListener("mousedown", this.regionDown);
this.canvas.region.addEventListener("mouseup", () => {
this.canvas.region.removeEventListener("mousemove", this.regionMove);
});
}
}
pointVal(val) {
this.tipVal = val;
}
}
export default createCanvas;