1 Star 0 Fork 0

zhangxiaoping/canvas

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
ImageHander.js 12.69 KB
一键复制 编辑 原始数据 按行查看 历史
zhangxiaoping 提交于 2024-09-03 01:30 . add ImageHander.js.
// 1.所有非标定点都能拖动--红色标记
// 2.所有标定点都不能拖动--绿色标记
// 3.选中点高亮为蓝色标记
const defaultSrc = './statics/potree/resources/pano_location.png';
const doneSrc = './statics/potree/resources/pano_icon_done.png';
const highLightSrc = './statics/potree/resources/pano_icon_biaoding.png';
class ImageHandler {
constructor(container) {
this.container = container;
const canvas = document.createElement('canvas');
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
container.appendChild(canvas);
this.canvasRef = canvas;
const { width, height } = this.canvasRef.getBoundingClientRect();
this.canvasRef.width = width;
this.canvasRef.height = height;
this.ctx = canvas.getContext('2d');
this.startPos = { x: 0, y: 0 };
this.imgX = 0;
this.imgY = 0;
this.isMove = false;
this.imgScale = 1;
this.MINIMUM_SCALE = 0.2;
this.MAX_SCALE = 5;
this.startPicking = false; // 开始选点
this.icons = [];
this.fixedIcons = [];
this.isDrag = false; // 拖动标记点
this.clickPosX = null;
this.clickPosY = null;
this.deltaT = null;
// this.listener = null;
this.defaultIcon = null;
this.doneIcon = null;
this.highLightIcon = null;
this.hoverIndex = null; //
this.selectedIcon = null;
this.loading = false;
this.disable = true; // 是否关闭画点功能
this.readyToDraw = false;
this.handleMap = new Map();
this.initCavas();
this.initIcon();
// requestAnimationFrame(this.update);
this.update();
}
async initCavas() {
this.canvasRef.addEventListener('mousedown', this.startMouse.bind(this));
this.canvasRef.addEventListener('mousemove', this.moveMouse.bind(this));
this.canvasRef.addEventListener('mouseup', this.endMouse.bind(this));
this.canvasRef.addEventListener('mousewheel', this.mouseWheel.bind(this)); // 监听滚轮
this.canvasRef.addEventListener('wheel', this.mouseWheel.bind(this)); // 监听滚轮
window.onresize = function(){
this.canvasRef.height = this.container.clientHeight;
this.canvasRef.width = this.container.clientWidth;
if(!this.img){
return;
}
this.fitScreen();
}.bind(this)
}
async initIcon() {
this.defaultIcon = await this.loadIcon(defaultSrc);
this.doneIcon = await this.loadIcon(doneSrc);
this.highLightIcon = await this.loadIcon(highLightSrc);
}
async load(path) {
this.imgX = 0;
this.imgY = 0;
this.imgScale = 1;
return await this.loadImage(path);
}
fitScreen() {
const canvasRatio = this.canvasRef.width / this.canvasRef.height;
const imgRatio = this.img.width / this.img.height;
if (imgRatio && imgRatio > canvasRatio) {
// 展示全部宽度,高度等比缩减 width = width / height取百分比
this.img.width = this.canvasRef.width;
this.img.height = (1 / imgRatio) * this.img.width;
this.imgY = (this.canvasRef.height - this.img.height) / 2;
} else {
// 展示全部高度
this.img.height = this.canvasRef.height;
this.img.width = imgRatio * this.canvasRef.height;
this.imgX = (this.canvasRef.width - this.img.width) / 2;
}
}
acceptImg(img) {
this.img = img;
this.readyToDraw = true;
}
loadImage(url) {
this.loading = true;
return new Promise((resolve, reject) => {
this.img = new Image();
this.img.crossOrigin = 'Anonymous';
this.img.onload = () => {
this.fitScreen();
this.readyToDraw = true;
resolve(this.img);
this.loading = false;
};
this.img.onerror = function (error) {
console.error(error, 'error=====');
reject(error);
};
this.img.src = url;
});
}
windowToCanvas(startX, startY) {
const { left, top, width, height } = this.canvasRef.getBoundingClientRect();
return {
x: startX - left - (width - this.canvasRef.width) / 2,
y: startY - top - (height - this.canvasRef.height) / 2,
};
}
drawImage() {
// 清除上一帧绘制
this.ctx.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height);
// 绘制图片
this.ctx.drawImage(
this.img,
0,
0,
this.img.naturalWidth,
this.img.naturalHeight,
this.imgX,
this.imgY,
this.img.width * this.imgScale,
this.img.height * this.imgScale,
);
}
startMouse(e) {
e.stopPropagation();
e.preventDefault();
if (e.button === 2) {
return;
}
const { pageX, pageY } = e;
this.clickPosX = e.pageX;
this.clickPosY = e.pageY;
this.deltaT = new Date().getTime();
// this.isMove = true;
const flag = this.calculateMove(e);
if (flag) {
const {index} = flag;
this.selectedIcon = this.icons[index];
this.selectedIcon.selectedIndex = index;
this.isDrag = true;
this.isMove = false;
} else {
this.isDrag = false;
this.isMove = true;
}
this.startPos = this.windowToCanvas(pageX, pageY);
}
moveMouse(e) {
e.stopPropagation();
e.preventDefault();
this.calculateMove(e);
if (this.isDrag && !this.isMove) {
// 拖动标记点
if (this.selectedIcon) {
this.moveIcon(e);
const handler = this.handleMap.get('mousemove');
if (handler) {
const data = this.exportCoord();
data.target = {
x: data.size.x * this.selectedIcon.position.left,
y: data.size.y * this.selectedIcon.position.top,
};
data.selectedIndex = this.selectedIcon.selectedIndex;
handler.call(this, data);
}
}
} else if (!this.isDrag && this.isMove) {
// 拖动背景图
const { pageX, pageY } = e;
this.movePos = this.windowToCanvas(pageX, pageY);
const x = this.movePos.x - this.startPos.x,
y = this.movePos.y - this.startPos.y;
this.imgX += x;
this.imgY += y;
this.startPos = { ...this.movePos }; // 更新最新位置
}
}
endMouse(e) {
e.stopPropagation();
e.preventDefault();
if (this.isDrag) {
this.isDrag = false;
this.isMove = false;
// 选中才能拖动
// this.selectedIcon = null;
return;
}
this.isMove = false;
if (
Math.abs(e.pageX - this.clickPosX) < 5 &&
Math.abs(e.pageY - this.clickPosY) < 5 &&
new Date().getTime() - this.deltaT < 1000
) {
this.pick(e);
}
}
mouseWheel(e) {
e.stopPropagation();
e.preventDefault();
const { clientX, clientY, wheelDelta } = e;
const pos = this.windowToCanvas(clientX, clientY);
// 计算图片的位置
const newPos = {
x: Number(((pos.x - this.imgX) / this.imgScale).toFixed(2)),
y: Number(((pos.y - this.imgY) / this.imgScale).toFixed(2)),
};
// 判断是放大还是缩小
if (wheelDelta > 0) {
// 放大
this.imgScale += 0.1;
if (this.imgScale >= this.MAX_SCALE) {
this.imgScale = this.MAX_SCALE;
}
} else {
// 缩小
this.imgScale -= 0.05;
if (this.imgScale <= this.MINIMUM_SCALE) {
this.imgScale = this.MINIMUM_SCALE;
}
}
// 计算图片的位置, 根据当前缩放比例,计算新的位置
this.imgX = (1 - this.imgScale) * newPos.x + (pos.x - newPos.x);
this.imgY = (1 - this.imgScale) * newPos.y + (pos.y - newPos.y);
// this.drawImage(); // 开始绘制图片
// this.drawIcons();
}
calculateMove(e) {
this.mouse = this.windowToCanvas(e.pageX, e.pageY);
let hoverTarget = null;
if (this.icons.length > 0) {
for (let i = 0; i < this.icons.length; i++) {
// this.icons[i].hover = false;
const position = this.icons[i].position;
const toLeft = this.img.width * this.imgScale * position.left;
const toTop = this.img.height * this.imgScale * position.top;
const width = toLeft + this.imgX;
const height = toTop + this.imgY;
document.body.style.cursor = 'default';
if (Math.abs(this.mouse.x - width) <= 9 && Math.abs(this.mouse.y - height) <= 9) {
// if (this.icons[i].hover) {
// document.body.style.cursor = 'grab';
// }
//去除鼠标滑过高亮,改为选中高亮
// this.icons[i].hover = true;
document.body.style.cursor = 'grab';
// this.selectedIcon = this.icons[i];
hoverTarget = { index: i, icon: this.icons[i] };
break;
}
}
}
return hoverTarget;
}
togglePick(flag) {
this.startPicking = flag;
}
pick(e) {
const flag = this.calculateMove(e);
if (flag) {
return;
}
if (this.disable) {
return;
}
const { left, top } = this.calculateMousePosition(e);
if (left < 0) {
return;
}
this.icons.push({ position: { left, top }, hover: false });
const handler = this.handleMap.get('click');
if (handler) {
const data = this.exportCoord();
data.target = data.list[data.list.length - 1];
handler.call(this, data);
}
}
async drawIcons() {
if (this.icons.length > 0) {
for (let i = 0; i < this.icons.length; i++) {
// icon.width = 18;
// icon.height = 18;
if (!this.icons[i].hover) {
this.icons[i].hover = false;
}
const icon = this.icons[i].hover ? this.highLightIcon : this.defaultIcon;
const { left, top } = this.icons[i].position;
const toLeft = this.imgX + this.img.width * this.imgScale * left;
const toTop = this.imgY + this.img.height * this.imgScale * top;
this.ctx.drawImage(
icon,
0,
0,
icon.width,
icon.height,
toLeft - 9,
toTop - 9,
18,
18,
// icon.width,
// icon.height
);
this.ctx.fillStyle = this.icons[i].hover ? 'blue' : 'red';
this.ctx.font = '16px Arial';
const number = i + 1;
if (number > 9) {
this.ctx.fillText(i + 1, toLeft - 9, toTop - 10);
} else {
this.ctx.fillText(i + 1, toLeft - 5, toTop - 10);
}
}
}
}
drawFixedIcons() {
if (this.fixedIcons.length > 0) {
for (let i = 0; i < this.fixedIcons.length; i++) {
const icon = this.doneIcon;
const { left, top } = this.fixedIcons[i].position;
const toLeft = this.imgX + this.img.width * this.imgScale * left;
const toTop = this.imgY + this.img.height * this.imgScale * top;
this.ctx.drawImage(
icon,
0,
0,
icon.width,
icon.height,
toLeft - 9,
toTop - 9,
18,
18,
);
this.ctx.fillStyle = 'rgb(0,255,0)';
this.ctx.font = '16px Arial';
const number = i + 1;
if (number > 9) {
this.ctx.fillText(i + 1, toLeft - 9, toTop - 10);
} else {
this.ctx.fillText(i + 1, toLeft - 5, toTop - 10);
}
}
}
}
loadIcon(src) {
return new Promise((resolve, reject) => {
const icon = new Image();
icon.crossOrigin = 'anonymous';
icon.src = src;
icon.onload = function () {
resolve(icon);
};
});
}
exportCoord() {
const size = { x: this.img.naturalWidth, y: this.img.naturalHeight };
const list = [];
if (this.icons.length > 0) {
this.icons.forEach(({ position }) => {
const { left, top } = position;
list.push({
x: this.img.naturalWidth * left,
y: this.img.naturalHeight * top,
});
});
}
return { size, list };
}
moveIcon(e) {
const { left, top } = this.calculateMousePosition(e);
if (left < 0 || top < 0) {
return;
}
this.selectedIcon.position.left = left;
this.selectedIcon.position.top = top;
}
removeIcons() {
this.ctx.clearRect(0, 0, this.canvasRef.width, this.canvasRef.height);
this.icons = [];
this.fixedIcons = [];
this.img = null;
this.readyToDraw = false;
}
calculateMousePosition(e) {
const { pageX, pageY } = e;
const position = this.windowToCanvas(pageX, pageY);
if (
position.x < this.imgX ||
position.y < this.imgY ||
position.x > this.imgX + this.img.width * this.imgScale ||
position.y > this.imgY + this.img.height * this.imgScale
) {
return { left: -1, top: -1 };
}
const left = (position.x - this.imgX) / (this.img.width * this.imgScale);
const top = (position.y - this.imgY) / (this.img.height * this.imgScale);
return { left, top };
}
addListener(key, cb) {
this.handleMap.set(key, cb);
}
update() {
if (this.loading) {
const handler = this.handleMap.get('loading');
if (handler) {
handler.call(this);
}
}
if (this.defaultIcon && this.doneIcon && this.img) {
const handler = this.handleMap.get('prepared');
if (handler) {
handler.call(this);
this.handleMap.delete('prepared');
}
}
if (this.img && this.readyToDraw) {
this.drawImage();
}
if (this.fixedIcons.length > 0) {
this.drawFixedIcons();
}
if (this.icons.length > 0) {
this.drawIcons();
}
const drawFn = this.handleMap.get('drawed');
if (this.fixedIcons.length > 0 && this.icons.length > 0 && drawFn) {
drawFn.call(this);
this.handleMap.delete('drawed');
}
requestAnimationFrame(this.update.bind(this));
}
}
export default ImageHandler;
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/zhangxiaopinga/canvas.git
[email protected]:zhangxiaopinga/canvas.git
zhangxiaopinga
canvas
canvas
master

搜索帮助