今天公司项目需要实现图像预览功能,所以尝试自己研究一波~~~~~~
预览图像功能比较简单没有涉及难的东西,本文旨在记录利用Teleport组件实现模态框预览图像的过程。其中包括知识点(Teleport组件的使用、模态框的样式、元素的拖拽以及滚轮控制放大缩小等)下面就逐步实现。
一、 视图层Teleport向body里插入模态框
Teleport组件:
vue3新增的组件,用来实现将元素插入到指定位置。适用于处理元素的代码与元素的位置不在同一组件的情况,例如:在某个组件中控制模态框的显示与隐藏,而模态框在body身上。
<!-- to属性用来指定插入的位置,类型为 string | HTMLElement -->
<Teleport to="#app">
<div class="modal" v-if="previewStatus" @wheel="wheelEvent">
<!-- 此处可以忽略无视, 项目中的pdf预览插件 -->
<vue-pdf-embed v-draggable :source="state.source" :style="scale" class="vue-pdf-embed" />
<button class="close" @click.prevent.stop="previewStatus = false">
<el-icon><Close /></el-icon>
</button>
<div class="handle-btns">
<button class="btn" @click.prevent.stop="pageZoomOut">
<el-icon><ZoomIn /></el-icon>
</button>
<button class="btn" @click.prevent.stop="pageZoomIn">
<el-icon><ZoomOut /></el-icon>
</button>
</div>
</div>
</Teleport>
二、 methods与模型层
import { ref, computed } from 'vue'
/** 模态框状态 */
const previewStatus = ref<boolean>(false)
/** 滚轮滚动事件 */
function wheelEvent (e:WheelEvent) {
requestAnimationFrame(() => {
const wheelModel = e.deltaY > 0 ? 'down' : 'up'
if (wheelModel === 'up') {
pageZoomOut()
} else if (wheelModel === 'down') {
pageZoomIn()
}
})
}
/** 控制图像缩放变量 */
const scale = computed(() => `transform:translate(-50%, -50%) scale(${state.scale});`)
/** 图像放大 */
function pageZoomOut () {
if (state.scale < 2) {
state.scale += 0.1
}
}
/** 图像缩小 */
function pageZoomIn () {
if (state.scale > 0.5) {
state.scale -= 0.1
}
}
/** ggable自定义拖拽指令的实现 */
interface ElType extends HTMLElement {
$fun?: (e:MouseEvent) => void
}
/**
* useTo: 拖拽
*/
const draggable = {
mounted (el:ElType) {
el.style.cursor = 'grab'
el.style.position = 'absolute'
el.$fun = (e:MouseEvent) => {
e.preventDefault()
// 计算当前元素距离浏览器左侧上侧的距离
const disX = e.clientX - el.offsetLeft
const disY = e.clientY - el.offsetTop
document.onmousemove = (e:MouseEvent) => {
// 计算鼠标移动的距离
const moveLeft = e.clientX - disX
const moveTop = e.clientY - disY
// 设置元素移动后的位置
el.style.left = moveLeft + 'px'
el.style.top = moveTop + 'px'
return false
}
document.onmouseup = () => {
document.onmousemove = null
document.onmouseup = null
}
}
el.addEventListener('mousedown', el.$fun)
},
beforeUnmount (el:ElType) {
if (!el.$fun) return
el.removeEventListener('mousedown', el.$fun)
delete el.$fun
}
}
/** 绑定到vue实例上
import { createApp } from 'vue'
const app = createApp(App)
app.use(install (app: App) {
app.directive('draggable', draggable)
})
三、 样式部分
.modal {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, .6);
z-index: 9999;
overflow: hidden;
.close {
position: absolute;
top: 10px;
right: 10px;
width: 46px;
height: 46px;
text-align: center;
line-height: 46px;
border-radius: 50%;
font-size: 24px;
color: #d0cece;
border: none;
background-color: rgba(0,0,0, .69);
cursor: pointer;
&:hover {
transform: scale(1.03);
}
}
.handle-btns {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
bottom: 68px;
left: 50%;
transform: translateX(-50%);
width: 180px;
height: 56px;
line-height: 56px;
border-radius: 30px;
background-color: rgba(0, 0, 0, .69);
.btn {
width: 40px;
height: 40px;
text-align: center;
line-height: 40px;
font-size: 25px;
color: #d0cece;
background-color: transparent;
border-radius: 50%;
border: none;
cursor: pointer;
&:hover {
background-color: rgba(255, 255, 255, .1)
}
}
}
.vue-pdf-embed {
position: absolute;
top: 30%;
left: 50%;
transform: translate(-50%, -50%);
width: 560px;
}
}