这是我参与更文挑战的第2天,活动详情查看:更文挑战
大图预览这个功能应该是比较常见的了,比如说在新闻阅读,博客阅读等,对文字中的图片,基本上都要有一个放大查看的功能,所以基于此,想要做一个大图预览模式的组件,做的不完美的地方希望大家可以指出~
其他先不说,上图,有兴趣的话再继续向下看:
1. 初始化Cli项目
使用vue create创建项目,项目目录如下:
在components下新建
img-large-mode文件夹,分别建立styles,utils,BgMask.vue,index.vue,然后也可以在components下建立一个index.js文件,供组件注册使用
2. img-large-mode
主要文件分析:
遮罩页面
<!-- BgMask.vue -->
<template>
<!-- 包裹动画 -->
<transition name="fade" v-if="visible">
<div>
<!-- 遮罩大背景,且通过监听window.resize事件进行自适应 -->
<div class="bg-mask" :style="{width: bodyWidth + 'px', height: bodyHeight + 'px'}" @click="closeAll">
</div>
<!-- 图片显示主题 自适应宽高且监听滚轮以进行缩放-->
<div class="content-box-body" :style="{width: bodyWidth + 'px', height: (bodyHeight - 100) + 'px'}" @click="closeAll" @mousewheel="handleMousewheel">
<img
:src="imgURL"
alt
class="img-custom"
ref="customImg"
:style="{
transform: `scale(${activeImg.scale}) rotate(${activeImg.rotate}deg)`,
transition: `${aniOpenFlag && aniName} 0.12s linear`
}"
/>
</div>
<!-- 工具按钮 -->
<div class="content-box-footer">
<abbr title="放大" @click="enLarge" v-if="_buttonOptions.enLargeButton">
<span class="iconfont icon-fangda icon"></span>
</abbr>
<abbr title="缩小" @click="narrowImg" v-if="_buttonOptions.narrowImgButton">
<span class="iconfont icon-suoxiao icon"></span>
</abbr>
<abbr title="还原" @click="initImg" v-if="_buttonOptions.initImgButton">
<span class="iconfont icon-huanyuan icon"></span>
</abbr>
<abbr title="左转" @click="rotate('left')" v-if="_buttonOptions.leftButton">
<span class="iconfont icon-xiangyouxuanzhuan icon"></span>
</abbr>
<abbr title="右转" @click="rotate('right')" v-if="_buttonOptions.rightButton">
<span class="iconfont icon-xiangzuoxuanzhuan icon"></span>
</abbr>
<abbr title="下载" v-if="_buttonOptions.downloadButton">
<span class="iconfont icon-xiazai icon"></span>
</abbr>
</div>
</div>
</transition>
</template>
<script>
//import x from ''
export default {
name: 'bg-mask',
components: {},
data() {
return {
// 动画开关
aniOpenFlag: true,
// 当前图片相关属性
activeImg: {
scale: 1,
rotate: 0
},
// 浏览器宽高
bodyWidth: window.innerWidth,
bodyHeight: window.innerHeight
}
},
props: {
// 按钮配置
buttonOptions: {
type: Object,
default: function () {
return {}
}
},
// 图片URL地址
imgData: {
default: ''
},
// 遮罩开关
visible: {
type: Boolean,
required: true
}
},
computed: {
// 按钮配置,合并传递的数据
_buttonOptions() {
let initOptions = {
enLargeButton: true,
narrowImgButton: true,
initImgButton: true,
leftButton: true,
rightButton: true,
downloadButton: true
}
return Object.assign(initOptions, this.buttonOptions)
},
// 图片地址
imgURL() {
if (Object.prototype.toString.call(this.imgData) == '[object String]') {
return this.imgData
} else {
return ''
}
// if (Object.prototype.toString.call(this.imgData) == '[object Array]') {
// return ''
// }
},
// 动画显隐,一般全开,主要在恢复原形时暂时为none
aniName() {
return this.aniOpenFlag ? 'all' : 'none'
}
},
created() {
// 监听窗口变化,随时改变遮罩与图片大小
window.addEventListener('resize', this.initMask)
},
methods: {
// 左转右转方法
rotate(direction) {
this.aniOpenFlag = true
let rotate = this.activeImg.rotate
rotate = direction == 'right' ? rotate + 90 : rotate - 90
this.activeImg.rotate = rotate
},
// 缩小方法
narrowImg() {
let scale = this.activeImg.scale
scale -= 0.1
if (scale <= 0.1) {
scale = 0.1
}
this.activeImg.scale = scale
},
// 放大方法
enLarge() {
let scale = this.activeImg.scale
scale += 0.1
if (scale >= 5) {
scale = 5
}
this.activeImg.scale = scale
},
// 监听滚轮,放大缩小
handleMousewheel(e) {
if (e.deltaY < 0) {
this.enLarge()
}
if (e.deltaY > 0) {
this.narrowImg()
}
},
// 初始化宽高
initMask() {
this.bodyWidth = window.innerWidth
this.bodyHeight = window.innerHeight
},
// 图片恢复方法
initImg() {
this.aniOpenFlag = false
this.activeImg = {
scale: 1,
rotate: 0
}
},
// 关闭方法
closeAll() {
this.initImg()
this.$emit('close')
}
}
}
</script>
<style lang='less' scoped>
...
</style>
<!-- components/img-large-mode/index.vue -->
<template>
<div class="img-large-mode">
<!-- 引入遮罩组件,配置相关属性与方法 -->
<bg-mask :visible.sync="visible" v-if="visible" @close="closeMask" :imgData="imgData" :buttonOptions="buttonOptions"></bg-mask>
</div>
</template>
<script>
//import x from ''
import BgMask from './BgMask'
export default {
components: {
BgMask
},
props: {},
data() {
return {
// 按钮配置
buttonOptions: {},
imgData: '',
visible: false
}
},
computed: {},
methods: {
// 打开遮罩方法
show(opt) {
// 对用户传入的的opt进行相关设置
if(opt) {
this.imgData = opt.imgData
this.buttonOptions = opt.buttonOptions
}
// 打开遮罩
this.visible = true
},
// 关闭遮罩方法
closeMask() {
this.visible = false
let imgLargeDomArr = document.querySelectorAll('.img-large-mode')
// 删除产生的dom
if (imgLargeDomArr.length > 0) {
for (let i = 0; i < imgLargeDomArr.length; i++) {
imgLargeDomArr[i].parentNode.removeChild(imgLargeDomArr[i])
}
}
}
}
}
</script>
styles是iconfot的css,utils里是工具函数,没几个方法,还在补充,目前不影响使用,就先不放了...
组件注册
/* components/index.js */
// 引入组件
import imgLargeMode from "@/components/img-large-mode";
// install方法,以供Vue.use()注册使用
const install = (Vue, option) => {
// 创建构造器,生成一个Vue实例
const componentInstance = Vue.extend(imgLargeMode);
// 定义实例化对象
let currentComponent = null;
// 对象实例化,将组件挂载到body下
const initInstance = () => {
currentComponent = new componentInstance();
let componentEL = currentComponent.$mount().$el;
document.body.appendChild(componentEL);
};
// 定义用户使用的方法,挂在到Vue
Vue.prototype.$_openLargeMode = {
show(opt) {
initInstance();
return currentComponent.show(opt);
}
};
};
if (typeof window !== "undefined" && window.Vue) {
install(window.Vue);
}
export default {
install,
imgLargeMode
};
然后在main.js中引入使用
...
import imgLargeMode from "@/components/index.js";
Vue.use(imgLargeMode);
...
简单使用
<template>
<div class="use">
<button @click="openMask">点击</button>
</div>
</template>
<script>
//import x from ''
export default {
components: {},
data() {
return {}
},
computed: {},
methods: {
openMask() {
this.$_openLargeMode.show({
imgData: `https://p1-jj.byteimg.com/tos-cn-i-t2oaga2asx/gold-user-assets/2019/12/7/16ee08923006f96b~tplv-t2oaga2asx-image.image`,
buttonOptions: {
downloadButton: false
}
})
}
}
}
</script>
<style lang='less' scoped>
//@import url()
</style>
OK,这个大图预览组件基本就完成了,有兴趣的可以试一下
这个算是第一版,还有很多不足的地方,回头慢慢升级,欢迎指正~
Demo源码地址: Github
文末感谢&参考文章: juejin.cn/post/696206…