403 lines
10 KiB
Vue

<template>
<div id="zoom-control">
<ul v-show="show" class="zoom-selecter">
<li v-for="(item, index) in zoomList" :key="index" :class="['zoom-item', { 'zoom-item-active': activezoomIndex === index }]" @click.stop="selectItem(index)">
<!-- <i v-if="item.icon" :class="['iconfont', item.icon]"></i> -->
<span>{{ item.text }}</span>
<i v-if="activezoomIndex === index" class="iconfont icon-selected"></i>
</li>
</ul>
<div v-if="!hideControl" class="zoom-control-wrap">
<div :class="['zoom-icon radius-left', { disable: activezoomIndex === 0 }]" @click.stop="activezoomIndex > 0 ? sub() : ''">
<i class="iconfont icon-sub"></i>
</div>
<div :class="['zoom-text', { 'zoom-text-active': show }]" @click.stop="show = !show">{{ zoom.text }}</div>
<div :class="['zoom-icon radius-right', { disable: otherIndex === otherList.length - 1 }]" @click.stop="otherIndex < otherList.length - 1 ? add() : ''">
<i class="iconfont icon-add"></i>
</div>
</div>
</div>
</template>
<script lang="ts">
import { mapGetters, mapActions } from 'vuex'
import { defineComponent } from 'vue'
import addMouseWheel from '@/common/methods/addMouseWheel'
// 组件大小控制器
const NAME = 'zoom-control'
let holder: number | undefined
// TODO: TS类型补全
export default defineComponent({
name: NAME,
data() {
return {
hideControl: false,
activezoomIndex: 0,
zoomList: [
{
text: '25%',
value: 25,
},
{
text: '50%',
value: 50,
},
{
text: '75%',
value: 75,
},
{
text: '100%',
value: 100,
},
{
text: '125%',
value: 125,
},
{
text: '150%',
value: 150,
},
{
text: '200%',
value: 200,
},
{
text: '最佳尺寸',
value: -1,
// icon: 'icon-best-size',
},
],
show: false,
zoom: {
value: 0,
text: 0,
},
otherList: [
{
text: '250%',
value: 250,
},
{
text: '300%',
value: 300,
},
{
text: '350%',
value: 350,
},
{
text: '400%',
value: 400,
},
{
text: '450%',
value: 450,
},
{
text: '500%',
value: 500,
},
],
otherIndex: -1,
bestZoom: 0,
curAction: "",
}
},
computed: {
...mapGetters(['dPage', 'dScreen', 'zoomScreenChange', 'dZoom']),
},
watch: {
activezoomIndex(value) {
if (value < 0 || value > this.zoomList.length - 1) {
return
}
this.zoom = JSON.parse(JSON.stringify(this.zoomList[value]))
},
otherIndex(value) {
if (value < 0 || value > this.otherList.length - 1) {
return
}
this.zoom = JSON.parse(JSON.stringify(this.otherList[value]))
},
zoom(value) {
let realValue = value.value
if (realValue === -1) {
realValue = this.calcZoom()
}
this.updateZoom(realValue)
this.autoFixTop()
},
dScreen: {
handler() {
this.screenChange()
},
deep: true,
},
zoomScreenChange() {
this.activezoomIndex = this.zoomList.length - 1
this.screenChange()
},
dPage: {
handler(val) {
this.screenChange()
},
deep: true,
},
},
async mounted() {
await this.$nextTick()
window.addEventListener('click', this.close)
if (this.$route.path === '/draw') {
this.activezoomIndex = 3
this.hideControl = true
} else {
this.activezoomIndex = this.zoomList.length - 1
}
// 添加滚轮监听
addMouseWheel('page-design', (isDown: boolean) => {
this.mousewheelZoom(isDown)
})
// 添加窗口大小监听
window.addEventListener('resize', (event) => {
this.changeScreen()
})
},
beforeUnmount() {
window.removeEventListener('click', this.close)
},
methods: {
...mapActions(['updateZoom', 'updateScreen']),
changeScreen() {
clearTimeout(holder)
holder = setTimeout(() => {
const screen = document.getElementById('page-design')
if (!screen) return
this.updateScreen({
width: screen.offsetWidth,
height: screen.offsetHeight,
})
}, 300)
},
screenChange() {
// 弹性尺寸即时修改
if (this.activezoomIndex === this.zoomList.length - 1) {
this.updateZoom(this.calcZoom())
this.autoFixTop()
}
},
selectItem(index: number) {
this.activezoomIndex = index
this.otherIndex = -1
this.show = false
},
close(_: MouseEvent) {
this.show = false
},
add() {
this.curAction = 'add'
this.show = false
if (this.activezoomIndex === this.zoomList.length - 2 || this.activezoomIndex === this.zoomList.length - 1) {
this.activezoomIndex = this.zoomList.length
// this.otherIndex += 1
if (this.bestZoom) {
this.nearZoom(true)
} else {
this.otherIndex += 1
}
return
}
if (this.activezoomIndex != this.zoomList.length) {
this.activezoomIndex++
return
}
if (this.otherIndex < this.otherList.length - 1) {
this.otherIndex++
}
},
sub() {
this.curAction = null as any
this.show = false
if (this.otherIndex === 0) {
this.otherIndex = -1
this.activezoomIndex = this.zoomList.length - 2
return
}
if (this.otherIndex != -1) {
this.otherIndex--
return
}
if (this.activezoomIndex === this.zoomList.length - 1) {
if (this.bestZoom) {
this.nearZoom()
} else {
this.activezoomIndex = this.zoomList.length - 2
}
return
}
if (this.activezoomIndex != 0) {
this.activezoomIndex--
}
},
mousewheelZoom(down: boolean) {
const value = Number(this.dZoom.toFixed(0))
if (down && value <= 1) return
this.updateZoom(down ? value - 1 : value + 1)
this.zoom.text = (value + '%') as any
this.autoFixTop()
},
nearZoom(add?: boolean) {
for (let i = 0; i < this.zoomList.length; i++) {
this.activezoomIndex = i
if (this.zoomList[i].value > this.bestZoom) {
if (add) break
} else if (this.zoomList[i].value < this.bestZoom) {
if (!add) break
}
}
this.bestZoom = 0
},
calcZoom() {
let widthZoom = ((this.dScreen.width - 142) * 100) / this.dPage.width
let heightZoom = ((this.dScreen.height - 122) * 100) / this.dPage.height
this.bestZoom = Math.min(widthZoom, heightZoom)
return this.bestZoom
},
async autoFixTop() {
await this.$nextTick()
const presetPadding = 60
const el = document.getElementById('out-page')
if (!el) return
// const clientHeight = document.body.clientHeight - 54
const parentHeight = (el.offsetParent as HTMLElement).offsetHeight - 54
let padding = (parentHeight - el.offsetHeight) / 2
if (typeof this.curAction === 'undefined') {
padding += presetPadding / 2
}
this.curAction === 'add' && (padding -= presetPadding)
this.$store.commit('updatePaddingTop', padding > 0 ? padding : 0)
},
},
})
</script>
<style lang="less" scoped>
@color-select: #1b1634;
@color1: #ffffff; // 选项板背景
@color2: #ffffff; // Appears 3 times
@color3: #666666; // 文字主颜色
@color4: #c2c2c2; // 禁用
@color5: rgba(0, 0, 0, 0.12); // 高亮选项背景
@z-border-color: #e6e6e6;
#zoom-control {
bottom: 20px;
position: absolute;
right: 302px;
z-index: 1000;
.zoom-control-wrap {
display: flex;
flex-direction: row;
font-size: 14px;
height: 40px;
.radius-left {
border-bottom-left-radius: 50%;
border-top-left-radius: 50%;
border-block-end: 1px solid @z-border-color;
border-block-start: 1px solid @z-border-color;
}
.radius-right {
border-bottom-right-radius: 50%;
border-top-right-radius: 50%;
border-block-end: 1px solid @z-border-color;
border-block-start: 1px solid @z-border-color;
}
.zoom-icon {
align-items: center;
background-color: @color2;
color: @color3;
cursor: pointer;
display: flex;
justify-content: center;
width: 40px;
&:hover {
background-color: @color1;
color: @color-select;
}
}
.disable {
color: @color4;
&:hover {
background-color: @color2;
color: @color4;
cursor: not-allowed;
}
}
.zoom-text {
user-select: none;
align-items: center;
background-color: @color2;
color: @color3;
cursor: pointer;
display: flex;
justify-content: center;
width: 60px;
border-block-end: 1px solid @z-border-color;
border-block-start: 1px solid @z-border-color;
&:hover {
background-color: @color1;
color: @color-select;
}
}
}
.zoom-selecter {
background-color: @color1;
color: @color3;
position: absolute;
top: -8px;
transform: translateY(-100%);
width: 100%;
z-index: 1000;
&:after {
bottom: -8px;
content: '';
left: 50%;
position: absolute;
transform: translateX(-50%);
}
.zoom-item {
align-items: center;
cursor: pointer;
display: flex;
font-size: 14px;
height: 34px;
padding: 10px;
width: 100%;
i {
margin-right: 10px;
&:last-child {
margin-right: 0;
}
}
span {
flex: 1;
}
&:hover {
background-color: @color5;
color: @color-select;
}
}
}
}
// #zoom-control-active {
// background-color: @color1;
// background-color: @color5;
// color: @color-select;
// color: @color-select;
// }
</style>