<template>
<div class="sImgEditorBox">
<div class="top" ref="viewDomP">
<a-spin :spinning="isShowLoading" class="fullBox">
<div class="fd-img-contain fd-clearfix" ref="imageContainer">
<div class="fd-img-panel" id="jsimgpanel" :style="panelStyle" @mousedown="imgbigsmalldown($event)" @mousemove="imgbigsmallmove($event)" @mouseup="imgbigsmallup($event)">
<img :src="currentImageUrl" :style="{width: imgWidth1 + 'px', height: imgHeight1 + 'px'}" class="fd-img">
<div :style="{width: imgWidth1 + 'px', height: imgHeight1 + 'px'}" class="fd-painting-drawing" id="jsdrawpaint" @mousedown="drawmaskdown($event)" @mousemove="drawmaskmove($event)" @mouseup="drawmaskup($event)">
<!-- 已确认的遮挡矩形 -->
<div v-for="(rect, index) in rectData"
:key="rect.id"
class="fdDiv"
:id="'jspzDiv' + rect.id"
:style="{
left: rect.x + 'px',
top: rect.y + 'px',
width: rect.width + 'px',
height: rect.height + 'px'
}">
</div>
<!-- 当前正在绘制的矩形 -->
<div v-if="currentRect"
class="fdDiv"
:style="{
left: currentRect.x + 'px',
top: currentRect.y + 'px',
width: currentRect.width + 'px',
height: currentRect.height + 'px'
}">
</div>
<!-- 确认操作面板 -->
<ul class="fd-pzselect"
:style="{
display: showConfirmPanel ? 'block' : 'none',
left: confirmPosition.x + 'px',
top: confirmPosition.y + 'px'
}">
<li class="fd-li" @click="clickPzSuredelete('y',$event)" @mousedown="mousedownSureDelete($event)" @mouseup="mouseupSureDelete($event)">确定</li>
<li class="fd-li" @click="clickPzSuredelete('n',$event)" @mousedown="mousedownSureDelete($event)" @mouseup="mouseupSureDelete($event)">取消</li>
</ul>
</div>
</div>
<canvas id="myCanvas" ref="myCanvas" v-show="active" :style="{width: imgWidth1, height: imgHeight1}"></canvas>
</div>
</a-spin>
<canvas class="fd-canvas-act" id="jsCanvasAct"></canvas>
</div>
<div class="bottom">
<div class="actionBox">
<span class="tip">{{isXsCpd?'提示:单击图片放大!':'提示:单击图片放大后,进行遮挡操作!'}}</span>
<div class="actionBox1" v-show="status!='WAIT_MAKE_DOCUMENT'&&status!='LEADER_APPROVED'">
<a
v-for="item in action"
:key="item.action"
:disabled="(!active && item.action !== 'upload') || item.disabled || isShowLoading"
:class="`btn ${
((!active && item.action !== 'upload') || item.disabled) &&
'disabledBox'
}`"
@click="onClickAction(item)"
>
<a-tooltip placement="bottom">
<template slot="title">
<span>{{ item.label }}</span>
</template>
<a-icon :type="item.icon" />
</a-tooltip>
</a>
</div>
</div>
<div class="viewList flex">
<a-tabs :activeKey="active" @change="onChangeTab">
<!-- <a-tab-pane key="upload">
<span slot="tab">
<div class="btnUpload" @click="onUpload">
<a-icon type="plus-circle" />
<input type="file" class="hideDom" ref="upload" />
</div>
</span>
</a-tab-pane> -->
<a-tab-pane v-for="item in imgs" :key="item.fileId">
<span slot="tab">
<img :src="item.src" class="viewListImg" />
</span>
</a-tab-pane>
</a-tabs>
</div>
</div>
<div class="fullscreen-container" v-show="isFullscreen">
<!-- 关闭按钮,右上角 -->
<a-button class="close-fullscreen-btn" type="danger" icon="close" @click="toggleFullScreen" />
<a-carousel
ref="carousel"
:dots="false"
:arrows="true"
v-model="carouselIndex"
class="image-carousel"
@change="handleCarouselChange"
>
<!-- 上一张箭头(左侧) -->
<div slot="prevArrow" slot-scope="props" class="custom-slick-arrow" style="left: 10px">
<a-icon type="left-circle" />
</div>
<div v-for="(item, index) in imgs" :key="item.fileId" class="image-wrapper">
<img :src="item.src" />
</div>
<!-- 下一张箭头(右侧) -->
<div slot="nextArrow" slot-scope="props" class="custom-slick-arrow" style="right: 10px">
<a-icon type="right-circle" />
</div>
</a-carousel>
</div>
</div>
</template>
<script>
import _ from "lodash";
export default {
components: {
},
props: {
dataSource: {
type: Array,
default: () => [],
},
disabled: {
type: Boolean,
default: false,
},
isXsCpd: { // 是否线上呈批单
type: Boolean,
default: false
}
},
data() {
this.onClickAction = _.debounce(this.onClickAction, 300);
return {
isShowLoading: false /* 是否显示loading */,
canvas: null /* canvas对象 */,
active: "" /* 当前编辑图片key */,
imgs: [
/* 图片list */
// {
// id: "1",
// src: "/files/1.png",
// },
// {
// id: "2",
// src: "/files/2.png",
// },
],
currentImageUrl: null,
currentImageName:null,
status: '', // 状态
width: "",
height: "",
rotation: 0, // 当前旋转角度(0、90、180、270)
canvas: null /* canvas对象 */,
ctx: null /* 2d画布对象 */,
cache: [] /* 缓存步骤数据 */,
cacheNum: 10 /* 缓存步骤数量 */,
cacheDoubleBack: false /* 添加完缓存之后点击返回要返回两次 */,
/**拖拽需要的参数 */
isMove: false, // 是否可移动
currntMoveX: 0, // 当前X轴偏移量
currntMoveY: 0, // 当前Y轴偏移量
scaleX: 0, // 拖拽起始X位置
scaleY: 0, // 拖拽起始Y位置
/**缩放需要的参数 */
scale: 1, // 当前缩放比例
minScale: 0.1, // 最小缩放比例
maxScale: 4, // 最大缩放比例
/**全屏参数 */
isFullscreen: false, // 是否全屏显示
carouselIndex: 0,
/**---------------------- */
// 预览的时候图片的大小
imgWidth1: 0,
imgHeight1: 0,
canvasDoc: null, //canvas的dom对象
/**遮挡功能参数--------------------------------------------- */
isClickZd: false, // 是否点击了遮挡按钮(进入遮挡模式)
isDown: false, // 鼠标是否按下(开始绘制矩形)
drawLeft: 0, // 鼠标按下的起始坐标(相对于绘制区域)
drawTop: 0, // 绘制起点Y坐标
drawIndex: 1, // 矩形索引计数器(每次绘制新矩形时递增,生成矩形的唯一ID)
rectData: [], // 存储已确认的遮挡矩形(每个矩形包含id, x, y, width, height)
currentRect: null, // 当前正在绘制的矩形(临时对象,包含x, y, width, height)
showConfirmPanel: false, // 是否显示确认面板
confirmPosition: { x: 0, y: 0 }, // 确认面板位置
zdbtnClass: false, // 控制遮挡按钮样式(表示激活状态)
};
},
computed: {
action() {
return [
/* 当前视图的操作 */
{
label: "全屏",
action: "fullscreen",
icon: "fullscreen",
disabled: this.disabled,
},
{
label: "上一个",
action: "prev",
icon: "left",
disabled: this.disabled,
},
{
label: "下一个",
action: "next",
icon: "right",
disabled: this.disabled,
},
{
label: "放大",
action: "zoomIn",
icon: "zoom-in",
disabled: this.disabled,
},
{
label: "缩小",
action: "zoomOut",
icon: "zoom-out",
disabled: this.disabled,
},
{
label: "逆时针旋转",
action: "rotateL",
icon: "undo",
disabled: this.disabled,
},
{
label: "顺时针旋转",
action: "rotateR",
icon: "redo",
disabled: this.disabled,
},
{
label: "撤回",
action: "back",
icon: "left",
disabled: this.disabled,
},
{
label: "重置",
action: "reset",
icon: "retweet",
disabled: this.disabled,
},
{
label: "下载",
action: "download",
icon: "download",
disabled: this.disabled,
},
{
label: "保存",
action: "save",
icon: "save",
disabled: this.disabled,
},
{
label: "遮挡",
action: "cover",
icon: "copy",
disabled: this.disabled,
},
{
label: "删除",
action: "delete",
icon: "delete",
disabled: this.disabled,
},
{
label: "上传",
action: "upload",
icon: "upload",
disabled: this.disabled,
},
];
},
panelStyle() {
return {
transform: `
translate(${this.currntMoveX}px, ${this.currntMoveY}px)
scale(${this.scale})
rotate(${this.rotation}deg)
`,
transformOrigin: 'center',
position: 'relative',
// cursor: this.isMove ? 'grabbing' : 'default',
};
}
},
watch: {
dataSource: {
handler(val) {
this.imgs = val || [];
let hasActive = _.find(this.imgs, { fileId: this.active });
if (hasActive) {
this.onloadImg(this.getItem(this.active)?.src, true, true);
return;
}
if (this.imgs.length > 0) {
this.onChangeTab(this.imgs[0]?.fileId);
} else {
this.active = null;
}
},
deep: true, // 深度监听
immediate: true, // 立即执行
},
},
created() {
this.status = this.$utils.getSearchKeys('status')
},
methods: {
onUpload() {
/* 点击上传摁扭 */
// let dom = this.$refs.upload;
// dom.click();
this.$emit("onUpload");
},
onChangeTab(e) {
/* 切换图片 */
if (e === this.active) return;
this.active = e;
// this.onloadImg(this.getItem(this.active)?.src, true, true);
this.resetImg()
this.resetData()
},
onClickAction(item) {
if (this.isShowLoading) return;
/* 点击当前视图操作 */
const { action } = item;
switch (action) {
case "fullscreen":
this.toggleFullScreen();
break;
case "prev":
this.prev();
break;
case "next":
this.next();
break;
case "zoomIn":
this.zoomIn();
break;
case "zoomOut":
this.zoomOut();
break;
case "rotateL":
this.rotateImage(-90);
break;
case "rotateR":
this.rotateImage(90);
break;
case "back":
this.onBackFromCache();
break;
case "reset":
this.onReset();
break;
case "download":
this.onSave("download");
break;
case "save":
this.onSave();
break;
case "cover":
this.onClickImgAction(item);
break;
case "upload":
this.$emit("onUpload");
break;
case "delete":
this.onDelete(item);
break;
}
},
onDelete() {
this.$emit("onDelete", this.getItem(this.active));
},
onReset() {
this.resetData();
this.$emit("onReset", this.getItem(this.active));
},
onSave(type) {
/* 保存、下载 */
const _this = this;
_this.isClickZd = false;
// 获取隐藏Canvas
const canvasAct = document.getElementById('jsCanvasAct');
if (!canvasAct) return;
const ctxAct = canvasAct.getContext('2d');
// 设置Canvas尺寸为原始图片尺寸
canvasAct.width = _this.width;
canvasAct.height = _this.height;
// 清空画布
ctxAct.fillStyle = "#fff";
ctxAct.fillRect(0, 0, canvasAct.width, canvasAct.height);
// 绘制原始图片
const img = new Image();
img.crossOrigin = 'anonymous'; // 如果图片跨域,需要设置
img.src = _this.currentImageUrl;
img.onload = function () {
ctxAct.drawImage(img, 0, 0, canvasAct.width, canvasAct.height);
// 绘制所有遮挡矩形
ctxAct.fillStyle = 'rgba(198,198,198,0.9)'; // 灰色半透明
_this.rectData.forEach(rect => {
// 将预览区域的矩形坐标转换到原始图片坐标
const ratioW = _this.width / _this.imgWidth1;
const ratioH = _this.height / _this.imgHeight1;
const ratio = Math.max(ratioW, ratioH);
const x = rect.x * ratio;
const y = rect.y * ratio;
const width = rect.width * ratio;
const height = rect.height * ratio;
ctxAct.fillRect(x, y, width, height);
});
// 生成Base64
var data = canvasAct.toDataURL('image/png');
if (type === "download") {
if(window.navigator.msSaveOrOpenBlob){
var bstr = atob(data.split(',')[1]);
var n = bstr.length;
var u8arr = new Uint8Array(n);
while(n--) {
u8arr[n] = bstr.charCodeAt(n);
}
var blob = new Blob([u8arr]);
window.navigator.msSaveOrOpenBlob(blob,_this.currentImageName)
}else {
let a = document.createElement("a");
a.download = _this.currentImageName;
a.href = data;
a.dispatchEvent(new MouseEvent("click"));
}
}
_this.$emit("onSave", { data, ..._this.getItem(_this.active) });
_this.resetData();
};
},
getItem(fileId) {
let item = _.find(this.imgs, { fileId });
return item;
},
// 获取合适的比例(要展示完全)
getImageRightProportion (imageWidth, imageHeight) {
const viewDomP = this.$refs.viewDomP;
const padding = 16;
const containerWidth = viewDomP.clientWidth - padding;
const containerHeight = viewDomP.clientHeight - padding;
const widthProportion = containerWidth / imageWidth;
const heightProportion = containerHeight / imageHeight;
const proportion = Math.min(widthProportion, heightProportion)
return Math.floor(proportion * 100) / 100;
},
onloadImg(path) {
this.currentImageUrl = path;
console.log('=============================path', path);
//创建图片
let image = new Image();
//设置图片地址
image.src = path + `?uuid=${this.$utils.getId()}`;
this.isShowLoading = true;
image.onload = (e) => {
const {width, height} = image;
this.width = image.width; // 原始宽高,用于重置比例计算,避免遮挡后重置比例失效问题
this.height = image.height;
const proportion = this.getImageRightProportion(width, height);
this.imgWidth1 = width * proportion;
this.imgHeight1 = height * proportion;
// 初始化Canvas,绘制图片
// this.initCanvas();
this.isShowLoading = false;
};
},
/**
* 遮挡相关-双Canvas策略
* 显示层:#myCanvas 用于实时预览
* 操作层:#jsCanvasAct 离屏处理实际遮挡合成
* 避免了频繁的服务器交互,所有遮挡处理均在客户端完成,只在最终保存时上传合成结果,大大提升了用户体验
* 进入遮挡模式 → 2. 绘制矩形区域 → 3. 确认保留矩形 → 4. 合成遮挡图 → 5. 上传服务器 → 6. 更新视图
* @param item
*/
onClickImgAction(item) {
if (this.isShowLoading) return;
this.zdbtnClass = !this.zdbtnClass;
this.isClickZd = !this.isClickZd;
},
drawmaskdown(event) {
if (this.isClickZd === false) return;
this.isDown = true;
// 获取目标元素的边界信息
const rect = event.currentTarget.getBoundingClientRect();
// 获取鼠标点击的相对位置
let x = event.clientX - rect.left;
let y = event.clientY - rect.top;
// 计算缩放后的实际位置
this.drawLeft = x / this.scale;
this.drawTop = y / this.scale;
// 初始化当前矩形
this.currentRect = {
id: this.drawIndex,
x: this.drawLeft,
y: this.drawTop,
width: 0,
height: 0
};
this.showConfirmPanel = false;
},
drawmaskmove(event) {
if (this.isClickZd === false || !this.isDown) return;
const rect = event.currentTarget.getBoundingClientRect();
// 获取鼠标当前位置
let offsetX = event.clientX - rect.left;
let offsetY = event.clientY - rect.top;
// 更新当前矩形的尺寸
this.currentRect.width = (offsetX / this.scale) - this.drawLeft;
this.currentRect.height = (offsetY / this.scale) - this.drawTop;
},
drawmaskup(event) {
if (this.isClickZd === false || !this.isDown) return;
this.isDown = false;
const rect = event.currentTarget.getBoundingClientRect();
// 获取鼠标抬起位置
let x = event.clientX - rect.left;
let y = event.clientY - rect.top;
// 显示确认面板(位置计算)
this.confirmPosition = {
x: x / this.scale + 20,
y: y / this.scale - 10
};
this.showConfirmPanel = true;
this.drawIndex++;
},
// 点击确定取消事件
clickPzSuredelete(yorn, event) {
// 保留原始功能
if (yorn === 'n') {
// 取消 - 丢弃当前矩形
this.currentRect = null;
this.drawIndex = this.drawIndex - 1;
} else {
// 确定 - 保存当前矩形
this.rectData.push({...this.currentRect});
this.currentRect = null;
}
// 隐藏确认面板
this.showConfirmPanel = false;
},
// 阻止确认面板上的鼠标事件冒泡
mousedownSureDelete:function (event) {
event.stopPropagation()
},
mouseupSureDelete:function (event) {
event.stopPropagation()
},
/**
* 移除旧的 Canvas 元素
* 创建新的 Canvas 元素并设置尺寸
* 加载并绘制新图片到 Canvas
* 清除之前绘制的遮挡元素
*/
initCanvas() {
// 绘制图片到 Canvas
this.canvasDoc = this.$refs.myCanvas;
if (this.canvasDoc) {
this.ctx = this.canvasDoc.getContext('2d');
this.canvasimg = new Image();
this.canvasimg.onload = () => {
// 清除旧内容
this.ctx.clearRect(0, 0, this.canvasDoc.width, this.canvasDoc.height);
// 绘制新图片
this.ctx.drawImage(this.canvasimg, 0, 0, this.imgWidth1, this.imgHeight1);
};
this.canvasimg.src = this.currentImageUrl;
}
// 清空数据
this.drawIndex = 1;
this.rectData = [];
this.isClickZd = false;
this.currentRect = null;
this.showConfirmPanel = false;
// 放大缩小操作数据还原
this.isMove = false; // 是否可移动
this.scale = 1; // 放大缩小倍数
this.currntMoveX = 0; // 移动left
this.currntMoveY = 0; // 移动top
this.scaleX = 0; //
this.scaleY = 0; //
this.rotation = 0; // 当前缩放
},
addCache() {
/* 添加缓存 */
let imgCache = this.ctx?.getImageData(
0,
0,
this.canvas.width,
this.canvas.height
);
this.cache.push(imgCache);
if (this.cache.length > this.cacheNum) {
this.cache.shift();
}
this.cacheDoubleBack = true;
},
/* 获取最新缓存 */
getCache() {
let imgCache;
if (this.cache.length === 1) {
imgCache = this.cache[0];
} else {
imgCache = this.cache.pop();
}
return imgCache;
},
onBackFromCache() {
/* 返回操作 */
let imgCache = this.getCache();
if (this.cacheDoubleBack) {
/* 如果需要返回两次,再拿一遍 */
imgCache = this.getCache();
this.cacheDoubleBack = false;
}
if (!imgCache) return;
this.ctx.putImageData(imgCache, 0, 0);
},
/**
* 旋转图片方法
* 重置缩放比例和位置偏移,清空遮挡
*/
rotateImage(degrees) {
if (this.isShowLoading) return;
// 退出遮挡模式
if (this.isClickZd) {
this.isClickZd = false;
this.zdbtnClass = false;
this.showConfirmPanel = false;
this.currentRect = null;
}
// 重置位置偏移量
this.currntMoveX = 0;
this.currntMoveY = 0;
// 更新旋转角度(确保在0-360度范围内)
this.rotation = (this.rotation + degrees + 360) % 360;
},
/**
* 负责将图片预览区域恢复到初始状态,并为新图片准备预览环境。
* 清除之前的遮挡区域
* 重置缩放和旋转状态
* 重新初始化 Canvas
* 设置新图片
*/
resetImg() {
this.rectData = []; // 清空之前绘制的矩形区域数据
this.isClickZd = false; // 关闭遮挡模式
// 加载并计算新图片尺寸
this.onloadImg(this.getItem(this.active)?.src, true, true);
this.$nextTick(() => {
// 初始化Canvas,绘制图片
this.initCanvas();
// 获取图片面板元素
const jsimgpanel = this.$refs.jsimgpanel;
if (jsimgpanel) {
// 重置位置
jsimgpanel.style.left = '0px';
jsimgpanel.style.top = '0px';
// 重置变换
jsimgpanel.style.transform = `rotate(0deg) scale(1)`;
}
});
},
resetData() {
// this.ctx?.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.rotation = 0;
this.cache = [];
this.canvas = null;
this.ctx = null;
// 重置拖拽状态
this.isMove = false;
this.currntMoveX = 0;
this.currntMoveY = 0;
// 重置缩放
this.scale = 1;
// 重置遮挡状态
this.rectData = []; // 清空已保存的矩形
this.isClickZd = false; // 退出遮挡模式
this.zdbtnClass = false; // 样式恢复
},
/**放大缩小相关方法 */
zoomIn() {
this.scale += 0.2;
if (this.scale > 5) this.scale = 5;
},
zoomOut() {
this.scale -= 0.2;
if (this.scale <= 0) this.scale = 0;
},
/**上一张下一张所需方法 */
prev(){
let index = this.imgs.findIndex(item => item.fileId == this.active)
if (index-1 >= 0) {
this.onChangeTab(this.imgs[index-1].fileId)
}else{
return
}
},
next(){
let index = this.imgs.findIndex(item => item.fileId == this.active)
if (index+1 < this.imgs.length) {
this.onChangeTab(this.imgs[index+1].fileId)
}else{
return
}
},
/**全屏所需方法 */
toggleFullScreen() {
if (!this.isFullscreen) {
// 打开全屏前设置当前索引
this.carouselIndex = this.imgs.findIndex(
img => img.fileId === this.active
);
// 如果找不到,默认显示第一张
if (this.carouselIndex === -1) {
this.carouselIndex = 0;
}
// 使用 $nextTick 确保 DOM 更新
this.$nextTick(() => {
// 手动设置轮播位置
if (this.$refs.carousel && this.$refs.carousel.goTo) {
this.$refs.carousel.goTo(this.carouselIndex);
}
});
}
this.isFullscreen = !this.isFullscreen;
},
handleCarouselChange(current) {
this.carouselIndex = current;
// 更新激活的图片
if (this.imgs[current]) {
this.active = this.imgs[current].fileId;
this.currentImageUrl = this.imgs[current].src;
this.currentImageName = this.imgs[current].downloadFileName;
}
},
/**
* 拖拽相关操作
*/
imgbigsmalldown(event) {
// 遮挡模式下禁用拖拽
if (this.isClickZd) return;
event.preventDefault();
this.isMove = true;
this.scaleX = event.clientX;
this.scaleY = event.clientY;
},
imgbigsmallmove(event) {
// 遮挡模式下禁用拖拽
if (this.isClickZd) return;
if (!this.isMove) return;
event.preventDefault();
// 计算移动距离
const deltaX = event.clientX - this.scaleX;
const deltaY = event.clientY - this.scaleY;
// 更新位置 - 使用响应式数据而非直接操作DOM
this.currntMoveX += deltaX;
this.currntMoveY += deltaY;
// 更新起始位置
this.scaleX = event.clientX;
this.scaleY = event.clientY;
},
imgbigsmallup() {
// 遮挡模式下禁用拖拽
if (this.isClickZd) return;
if (!this.isMove) return;
this.isMove = false;
},
},
};
</script>
<style lang="less" scoped>
.sImgEditorBox {
width: 100%;
height: 100%;
}
.top {
height: calc(100% - 120px);
padding-top: 16px;
width: 100%;
text-align: center;
overflow: auto;
}
.bottom {
position: relative;
height: 120px;
background: #fff;
z-index: 1;
.actionBox {
height: 40px;
border-top: 1px solid #dadada;
border-bottom: 1px solid #dadada;
display: flex;
justify-content: space-between;
}
.tip {
display: flex;
align-items: center;
padding-left: 8px;
color: red;
}
.actionBox1 {
text-align: right;
display: flex;
align-items: center;
a,
div {
display: inline-block;
margin-right: 16px;
font-size: 20px;
}
}
.viewList {
height: calc(100% - 40px);
padding: 0 16px;
.viewListImg,
.btnUpload {
display: inline-block;
width: 40px;
height: 60px;
}
.btnUpload {
border: 1px dashed #2160b8;
display: flex;
justify-content: center;
align-items: center;
}
}
}
/deep/ .anticon {
margin: 0 !important;
}
/deep/ .ant-tabs-tab {
padding: 14px 5px !important;
}
/deep/ .ant-tabs,
/deep/.ant-tabs-bar,
/deep/ .ant-tabs-nav-container,
/deep/.ant-tabs-nav-wrap,
/deep/ .ant-tabs-nav-scroll,
/deep/.ant-tabs-nav {
height: 100%;
}
.hideDom {
display: inline-block;
visibility: hidden;
width: 0px;
height: 0px;
}
.btn {
color: #555;
}
.btn:hover {
color: #1890ff;
}
.selBtn {
color: #1890ff;
}
.disabledBox {
color: #ddd;
}
/**全屏样式================================ */
// 全屏容器
.fullscreen-container {
position: fixed;
top: 0;
left: 0;
z-index: 1000;
width: 100%;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.6);
// 轮播图容器
.image-carousel {
width: 100%;
height: 100%;
.slick-slide {
display: flex !important;
justify-content: center;
align-items: center;
height: 100%;
}
// 自定义左右箭头样式
.custom-slick-arrow {
position: absolute;
z-index: 1000;
top: 50%;
transform: translateY(-50%);
width: 60px;
height: 100px;
display: flex !important;
align-items: center;
justify-content: center;
background: rgba(0, 0, 0, 0.4);
border-radius: 5px;
font-size: 32px;
color: white;
transition: all 0.3s ease;
&:before {
display: none;
}
&:hover {
background: rgba(0, 0, 0, 0.7);
transform: translateY(-50%) scale(1.1);
}
}
// 左侧箭头
[slot="prevArrow"] {
left: 10px;
}
// 右侧箭头
[slot="nextArrow"] {
right: 10px;
}
}
}
// 图片包装
.image-wrapper {
// max-width: 90%;
// max-height: 90%;
overflow: auto;
display: flex !important;
justify-content: center;
align-items: center;
height: 100%;
img {
max-width: 100%;
max-height: 100vh;
object-fit: contain;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.3);
}
}
// 关闭按钮样式(右上角)
.close-fullscreen-btn {
position: absolute;
top: 20px;
right: 20px;
z-index: 1001; // 确保在最上层
background: rgba(255, 255, 255, 0.3);
border: none;
color: white;
font-size: 24px;
width: 48px;
height: 48px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: all 0.3s ease;
&:hover {
background: rgba(255, 0, 0, 0.7);
transform: scale(1.1);
}
}
/*********图片展示部分********/
.fd-img-contain{
position: relative;
z-index: 1;
margin: 0 auto;
padding: 15px 0;
width: 515px;
height: 100%;
}
.fd-img-panel {
position: absolute;
left: 0;
top: 0;
z-index: 5;
width: 100%;
height: 100%;
display: flex;
align-items: center; /*定义body的元素垂直居中*/
justify-content: center; /*定义body的里的元素水平居中*/
}
.fd-img-panel .fd-painting-drawing {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
width: 100%;
height: 100%;
}
// 遮挡矩形
.fdDiv {
width: 1px;
height: 1px;
border: 1px solid #333333;
position: absolute;
background: rgba(198,198,198,0.9);
}
/***********批注选择样式**********/
.fd-pzselect {
display: none;
position: absolute;
top: 9px;
left: 99px;
z-index: 8;
width: 84px;
border: 1px solid rgb(167, 171, 176);
background: rgb(245, 245, 245);
}
/**canvas样式============== */
#myCanvas {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 2;
opacity: 0;
}
.fd-canvas-act {
display: none;
}
</style>
这个是我修改完的,旋转后再遮挡,正在绘制的遮挡矩形开始位置与我鼠标按下位置不一样,并且正在绘制的遮挡矩形跟确认面板是不需要跟着旋转的,用户看到的是旋转后的图片,那么就在旋转后的图片上进行遮挡操作,我理解旋转不应该影响到遮挡操作