|
@@ -1,12 +1,5 @@
|
|
|
<template>
|
|
|
- <!-- 进度条 -->
|
|
|
- <!-- <el-progress
|
|
|
- :percentage="progress"
|
|
|
- :status="progress === 100 ? 'success' : ''"
|
|
|
- :stroke-width="10"
|
|
|
- :text-inside="true"
|
|
|
- style="width: 100%; margin-bottom: 20px;"
|
|
|
- /> -->
|
|
|
+
|
|
|
<div ref="threeContainer" class="three-container" :style="{height: height}">
|
|
|
<!-- 添加色卡Canvas -->
|
|
|
<canvas
|
|
@@ -14,6 +7,7 @@
|
|
|
ref="colorCardCanvas"
|
|
|
class="color-card-canvas"
|
|
|
:style="colorCardStyle"
|
|
|
+ @mousedown="startDrag"
|
|
|
></canvas>
|
|
|
</div>
|
|
|
</template>
|
|
@@ -53,6 +47,10 @@ const threeContainer = ref(null);
|
|
|
const colorCardCanvas = ref(null)
|
|
|
const colorCardContext = ref(null)
|
|
|
const progress = ref(0); // 进度条的值
|
|
|
+// 添加拖拽相关状态
|
|
|
+const isDragging = ref(false)
|
|
|
+const dragStartPos = ref({ x: 0, y: 0 })
|
|
|
+const currentPos = ref({ x: 0.85, y: 0.85 }) // 初始位置
|
|
|
|
|
|
let scene, camera, renderer, controls
|
|
|
|
|
@@ -156,28 +154,117 @@ const renderVTK = (data) => {
|
|
|
|
|
|
}
|
|
|
|
|
|
+// 开始拖拽
|
|
|
+const startDrag = (e) => {
|
|
|
+ if (!colorCardCanvas.value) return
|
|
|
+
|
|
|
+ const containerRect = threeContainer.value.getBoundingClientRect()
|
|
|
+ const cardRect = colorCardCanvas.value.getBoundingClientRect()
|
|
|
+
|
|
|
+ // 检查点击是否在色卡上
|
|
|
+ if (
|
|
|
+ e.clientX < cardRect.left ||
|
|
|
+ e.clientX > cardRect.right ||
|
|
|
+ e.clientY < cardRect.top ||
|
|
|
+ e.clientY > cardRect.bottom
|
|
|
+ ) {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ isDragging.value = true
|
|
|
+ dragStartPos.value = {
|
|
|
+ x: e.clientX,
|
|
|
+ y: e.clientY
|
|
|
+ }
|
|
|
+
|
|
|
+ // 使用passive: false确保能调用preventDefault
|
|
|
+ window.addEventListener('mousemove', handleDrag, { passive: false })
|
|
|
+ window.addEventListener('mouseup', stopDrag, { passive: true })
|
|
|
+ e.preventDefault()
|
|
|
+}
|
|
|
+
|
|
|
+// 添加拖拽边界限制
|
|
|
+const handleDrag = (e) => {
|
|
|
+ if (!isDragging.value || !colorCardCanvas.value) return
|
|
|
+
|
|
|
+ const containerRect = threeContainer.value.getBoundingClientRect()
|
|
|
+ const cardRect = colorCardCanvas.value.getBoundingClientRect()
|
|
|
+
|
|
|
+ const dx = e.clientX - dragStartPos.value.x
|
|
|
+ const dy = e.clientY - dragStartPos.value.y
|
|
|
+
|
|
|
+ // 计算新位置(像素值)
|
|
|
+ let newX = (currentPos.value.x * containerRect.width) + dx
|
|
|
+ let newY = (currentPos.value.y * containerRect.height) + dy
|
|
|
+
|
|
|
+ // 边界检查(确保色卡完全留在容器内)
|
|
|
+ newX = Math.max(cardRect.width/2, Math.min(containerRect.width - cardRect.width/2, newX))
|
|
|
+ newY = Math.max(cardRect.height/2, Math.min(containerRect.height - cardRect.height/2, newY))
|
|
|
+
|
|
|
+ // 转换为相对位置 (0-1)
|
|
|
+ currentPos.value.x = newX / containerRect.width
|
|
|
+ currentPos.value.y = newY / containerRect.height
|
|
|
+
|
|
|
+ dragStartPos.value = { x: e.clientX, y: e.clientY }
|
|
|
+ e.preventDefault() // 防止默认行为
|
|
|
+
|
|
|
+ // 强制更新样式
|
|
|
+ colorCardCanvas.value.style.left = `${newX}px`
|
|
|
+ colorCardCanvas.value.style.top = `${newY}px`
|
|
|
+}
|
|
|
+
|
|
|
+// 停止拖拽
|
|
|
+const stopDrag = () => {
|
|
|
+ isDragging.value = false
|
|
|
+ window.removeEventListener('mousemove', handleDrag)
|
|
|
+ window.removeEventListener('mouseup', stopDrag)
|
|
|
+}
|
|
|
+
|
|
|
+// 更新色卡位置
|
|
|
+const updateColorCardPosition = () => {
|
|
|
+ // 更新skvalue中的位置值
|
|
|
+ skvalue.value.X = currentPos.value.x.toFixed(2)
|
|
|
+ skvalue.value.Y = currentPos.value.y.toFixed(2)
|
|
|
+
|
|
|
+ // 触发色卡重新渲染
|
|
|
+ if (pltData.value?.config?.colorCard) {
|
|
|
+ pltData.value.config.colorCard.X = currentPos.value.x
|
|
|
+ pltData.value.config.colorCard.Y = currentPos.value.y
|
|
|
+ pltData.value = {...pltData.value}
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
// 计算色卡显示状态和样式
|
|
|
const showColorCard = computed(() => {
|
|
|
return props.data?.config?.colorCard?.visible
|
|
|
})
|
|
|
|
|
|
const colorCardStyle = computed(() => {
|
|
|
- if (!showColorCard.value) return {}
|
|
|
+ if (!showColorCard.value || !threeContainer.value) return {}
|
|
|
const config = props.data.config.colorCard
|
|
|
+
|
|
|
+ // 确保最小尺寸
|
|
|
+ const minWidth = 70 // 最小宽度(px)
|
|
|
+ const minHeight = 25 // 最小高度(px)
|
|
|
+
|
|
|
+ // 计算实际尺寸(取配置值和最小值的较大者)
|
|
|
+ const width = config.orientation === 'vertical'
|
|
|
+ ? `${Math.max(minWidth, config.width * 100)}px`
|
|
|
+ : `${Math.max(minWidth, config.width * 100)}%`
|
|
|
+
|
|
|
+ const height = config.orientation === 'vertical'
|
|
|
+ ? `${Math.max(minHeight, config.height * 100)}%`
|
|
|
+ : `${Math.max(minHeight, config.height * 100)}px`
|
|
|
+
|
|
|
return {
|
|
|
position: 'absolute',
|
|
|
- [config.orientation === 'vertical' ? 'right' : 'bottom']: '10px',
|
|
|
- [config.orientation === 'vertical' ? 'top' : 'left']: '50%',
|
|
|
- transform: config.orientation === 'vertical'
|
|
|
- ? 'translateY(-50%)'
|
|
|
- : 'translateX(-50%)',
|
|
|
- width: config.orientation === 'vertical'
|
|
|
- ? `${config.width * 100}px`
|
|
|
- : `${config.width * 100}%`,
|
|
|
- height: config.orientation === 'vertical'
|
|
|
- ? `${config.height * 100}%`
|
|
|
- : `${config.height * 100}px`,
|
|
|
- zIndex: 1000
|
|
|
+ left: `${currentPos.value.x * 100}%`,
|
|
|
+ top: `${currentPos.value.y * 100}%`,
|
|
|
+ transform: 'translate(-50%, -50%)',
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ zIndex: 1000,
|
|
|
+ cursor: 'move'
|
|
|
}
|
|
|
})
|
|
|
// 初始化色卡
|
|
@@ -193,18 +280,37 @@ const renderColorCard = () => {
|
|
|
|
|
|
const config = props.data.config.colorCard
|
|
|
const ctx = colorCardContext.value
|
|
|
- const width = colorCardCanvas.value.width
|
|
|
- const height = colorCardCanvas.value.height
|
|
|
+
|
|
|
+ // 根据设备像素比调整canvas分辨率
|
|
|
+ const dpr = window.devicePixelRatio || 1
|
|
|
+ const width = colorCardCanvas.value.clientWidth * dpr
|
|
|
+ const height = colorCardCanvas.value.clientHeight * dpr
|
|
|
+
|
|
|
+ // 设置canvas实际尺寸
|
|
|
+ colorCardCanvas.value.width = width
|
|
|
+ colorCardCanvas.value.height = height
|
|
|
+
|
|
|
+ // 缩放绘图上下文
|
|
|
+ ctx.scale(dpr, dpr)
|
|
|
+
|
|
|
+ if (config.orientation === 'vertical') {
|
|
|
+ canvas.width = 60 * (window.devicePixelRatio || 1)
|
|
|
+ canvas.height = threeContainer.value.clientHeight * 0.35 * (window.devicePixelRatio || 1)
|
|
|
+ } else {
|
|
|
+ canvas.width = threeContainer.value.clientWidth * 0.35 * (window.devicePixelRatio || 1)
|
|
|
+ canvas.height = 60 * (window.devicePixelRatio || 1)
|
|
|
+ }
|
|
|
|
|
|
// 清除画布
|
|
|
ctx.clearRect(0, 0, width, height)
|
|
|
|
|
|
- // 创建渐变
|
|
|
+ // 创建渐变(根据朝向)
|
|
|
+ // 根据方向创建渐变
|
|
|
let gradient
|
|
|
if (config.orientation === 'vertical') {
|
|
|
- gradient = ctx.createLinearGradient(0, height, 0, 0)
|
|
|
+ gradient = ctx.createLinearGradient(0, canvas.height, 0, 0)
|
|
|
} else {
|
|
|
- gradient = ctx.createLinearGradient(0, 0, width, 0)
|
|
|
+ gradient = ctx.createLinearGradient(0, 0, canvas.width, 0)
|
|
|
}
|
|
|
|
|
|
// 添加色卡颜色
|
|
@@ -216,11 +322,12 @@ const renderColorCard = () => {
|
|
|
ctx.fillStyle = gradient
|
|
|
ctx.fillRect(0, 0, width, height)
|
|
|
|
|
|
- // 添加刻度
|
|
|
+ // 设置更清晰的文字样式
|
|
|
ctx.strokeStyle = '#000'
|
|
|
- ctx.lineWidth = 1
|
|
|
- ctx.font = `${config.fontSize}px ${config.font}`
|
|
|
- ctx.fillStyle = '#000'
|
|
|
+ ctx.lineWidth = 2 // 加粗刻度线
|
|
|
+ ctx.font = `bold ${config.fontSize}px ${config.font}` // 加粗字体
|
|
|
+ ctx.fillStyle = '#FFF' // 改为白色文字更清晰
|
|
|
+ ctx.textBaseline = 'middle'
|
|
|
|
|
|
// 根据朝向绘制刻度
|
|
|
if (config.orientation === 'vertical') {
|
|
@@ -233,10 +340,13 @@ const renderColorCard = () => {
|
|
|
ctx.lineTo(width, y)
|
|
|
ctx.stroke()
|
|
|
|
|
|
- // 跳过指定层级的标签
|
|
|
if (i % (config.skipLevels + 1) === 0) {
|
|
|
const value = (1 - i / height * 1).toFixed(config.precision)
|
|
|
- ctx.fillText(value, 5, y + 5)
|
|
|
+ // 添加文字背景增强可读性
|
|
|
+ ctx.fillStyle = 'rgba(0,0,0,0.5)'
|
|
|
+ ctx.fillRect(5, y - 10, 50, 20)
|
|
|
+ ctx.fillStyle = '#FFF'
|
|
|
+ ctx.fillText(value, 10, y)
|
|
|
}
|
|
|
}
|
|
|
} else {
|
|
@@ -249,29 +359,43 @@ const renderColorCard = () => {
|
|
|
ctx.lineTo(x, height * 0.3)
|
|
|
ctx.stroke()
|
|
|
|
|
|
- // 跳过指定层级的标签
|
|
|
if (i % (config.skipLevels + 1) === 0) {
|
|
|
const value = (i / width * 1).toFixed(config.precision)
|
|
|
- ctx.fillText(value, x - 10, height - 5)
|
|
|
+ // 添加文字背景
|
|
|
+ ctx.fillStyle = 'rgba(0,0,0,0.5)'
|
|
|
+ ctx.fillRect(x - 15, height - 25, 30, 20)
|
|
|
+ ctx.fillStyle = '#FFF'
|
|
|
+ ctx.fillText(value, x - 10, height - 15)
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 添加标题
|
|
|
+ // 添加标题(增强样式)
|
|
|
if (config.showTitle) {
|
|
|
ctx.font = `bold ${config.titleFontSize}px ${config.titleFont}`
|
|
|
ctx.textAlign = 'center'
|
|
|
+ ctx.fillStyle = '#FFF'
|
|
|
+
|
|
|
const title = config.titleSource === 'custom'
|
|
|
? config.customTitle
|
|
|
: 'Color Scale'
|
|
|
|
|
|
if (config.orientation === 'vertical') {
|
|
|
+ // 添加标题背景
|
|
|
+ ctx.fillStyle = 'rgba(0,0,0,0.7)'
|
|
|
+ ctx.fillRect(15, height/2 - 15, 30, 30)
|
|
|
+ ctx.fillStyle = '#FFF'
|
|
|
+
|
|
|
ctx.save()
|
|
|
- ctx.translate(20, height / 2)
|
|
|
+ ctx.translate(30, height / 2)
|
|
|
ctx.rotate(-Math.PI / 2)
|
|
|
ctx.fillText(title, 0, 0)
|
|
|
ctx.restore()
|
|
|
} else {
|
|
|
+ // 添加标题背景
|
|
|
+ ctx.fillStyle = 'rgba(0,0,0,0.7)'
|
|
|
+ ctx.fillRect(width/2 - 50, height - 30, 100, 25)
|
|
|
+ ctx.fillStyle = '#FFF'
|
|
|
ctx.fillText(title, width / 2, height - 15)
|
|
|
}
|
|
|
}
|
|
@@ -567,6 +691,8 @@ const adjustCameraForPlt = (scene, camera, params = {}) => {
|
|
|
<style scoped>
|
|
|
.three-container {
|
|
|
width: 100%;
|
|
|
+ position: relative;
|
|
|
+ overflow: hidden;
|
|
|
/* height: calc(60vh - 6px); */
|
|
|
border: 1px solid #ccc; /* 可选:添加边框以便查看容器范围 */
|
|
|
}
|
|
@@ -574,8 +700,24 @@ const adjustCameraForPlt = (scene, camera, params = {}) => {
|
|
|
font-size: 12px;
|
|
|
}
|
|
|
.color-card-canvas {
|
|
|
- pointer-events: none; /* 允许点击穿透 */
|
|
|
- border: 1px solid #ccc;
|
|
|
- background: white;
|
|
|
+ min-width: 70px;
|
|
|
+ min-height: 25px;
|
|
|
+ position: absolute;
|
|
|
+ will-change: transform;
|
|
|
+ user-select: none;
|
|
|
+ touch-action: none;
|
|
|
+ cursor: move;
|
|
|
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);
|
|
|
+ transition: transform 0.1s ease-out;
|
|
|
+}
|
|
|
+
|
|
|
+.color-card-canvas:hover {
|
|
|
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
|
|
+}
|
|
|
+
|
|
|
+.color-card-canvas:active {
|
|
|
+ cursor: grabbing;
|
|
|
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
|
|
|
+ transition: none;
|
|
|
}
|
|
|
</style>
|