龙空技术网

vue 封装图片预览组件 图片放大、缩小、旋转

爱码字的小屁孩 95

前言:

此时各位老铁们对“vue 翻页效果”大体比较关心,兄弟们都想要学习一些“vue 翻页效果”的相关内容。那么小编同时在网络上汇集了一些对于“vue 翻页效果””的相关文章,希望我们能喜欢,你们快快来了解一下吧!

封装步骤插入body,制作全局遮罩层、禁止背景滚动

创建一个组件插入body中,使用固定定位生成一个全局遮罩层,再把图片放进去

创建组件/components/PreviewImage/index.vue

搭建组件结构,传入url数组,以及打开的当前图片索引下标,以便进行翻页查看

如果遮罩层后面页面有滚动条时在组件打开时,需要禁止背景内容随鼠标滚轮滚动,每次打开关闭时给body动态增加样式 overflow: hidden属性即可

<template>  <div v-if="show" class="previewImage_wrapper">      <div class="previewImage_image">        <img :src="previewImgList[currentIndex] || ''">      </div>      <div class="previewImage_close previewImage_btn" @click="closePreviewImage">×</div>      <div class="previewImage_navigation">        <span class="previewImage_navigation_left previewImage_btn" @click="prevImage"><</span>        <span class="previewImage_navigation_right previewImage_btn" @click="nextImage">></span>      </div>    </div></template><script>export default {  props: {    visible: { // 显示控制      type: Boolean,      default: false    },    previewImgList: { // url数组      type: Array,      default: () => []    },    currentIndex: { // 当前图片索引      type: Number,      default: 0    }  },  computed: {    // 双向绑定    show: {      get() {        return this.visible      },      set(newVal) {        this.$emit('update:visible', newVal)      }    }  },  watch: {    visible: { // 给body动态增加style属性,禁止背景内容的鼠标滚轮滚动      handler(newVal) {        if(newVal) {          document.body.style.overflow = "hidden";        } else {          document.body.style.overflow = "";        }      }    },  },  methods: {    // 上一张图片    prevImage() {      if (this.currentIndex === 0) {        this.currentIndex = this.previewImgList.length - 1      } else {        this.currentIndex--      }    },    // 下一张图片    nextImage() {      if (this.currentIndex === this.previewImgList.length - 1) {        this.currentIndex = 0      } else {        this.currentIndex++      }    },    // 关闭预览图片组件    closePreviewImage() {      this.show = false    }  },  mounted() { // 插入body    document.body.appendChild(this.$el);  },  destroyed() { // 组件销毁后同步清除元素    this.$el.parentNode.removeChild(this.$el);  }}
定义组件过渡动画

在打开遮罩层时加上过渡效果,让组件体验更好

定义过渡动画,使用vue推荐使用自带的 组件,这样切换显示隐藏都会触发过渡效果,如果以class类名的形式定义的过渡动画,在使用指令时v-if或者v-show 隐藏关闭时不会触发结束的过渡效果

<transition name="zoom">    <!-- 组件 --></transition>
.zoom-enter, .zoom-leave-to { // 元素进入和离开时的动作  transform: scale(0);}.zoom-enter-active, .zoom-leave-active { // 元素进入和离开时的过渡动画定义  transition: transform 0.3s;}

效果如下

效果实现了,接下来还可以加入更多的功能

结合 transform 实现图片控制

在图片底部加一个控制工具栏,例如对预览图片的控制,放大、缩小、翻转等。在封装组件实现功能的时候我们应当先实现基础功能,再深入开发细节功能

想要实现元素的放大缩小,翻转,可以直接利用css3中transform属性中的 scale rotate,然后使用 js 进行动态控制

<script>export default {    data() {      return {        imgHandle: { // 图片控制          scale: 1,          rotate: 0        }      }    },    methods: {    // 初始化还原图片缩放旋转控制    async initImgHandle() {      this.imgHandle = {        scale: 1,        rotate: 0      }      await this.$nextTick()      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    // 放大图片    async largeHandle() {      console.log(this.imgHandle.scale, 'scale')      this.imgHandle.scale = Number((this.imgHandle.scale + 0.2).toFixed(2)) // 使用toFixed防止小数点精度不准      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    // 缩小图片    async shrinkHandle() {      console.log(this.imgHandle.scale, 'scale')      if (this.imgHandle.scale === 0.2) { // 最低缩放到0.2倍        return      }      this.imgHandle.scale = Number((this.imgHandle.scale - 0.2).toFixed(2)) // 使用toFixed防止小数点精度不准      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    }}</script>

在进行小数点计算的时候,要注意小数点的精度问题,不然可能会导致计算出错产生bug,这里使用 toFixed 来解决下

在 JavaScript 中处理小数计算时,会遇到舍入误差导致计算结果不准确的情况。这是由于 JavaScript 中采用的是双精度浮点数格式(IEEE 754 标准)来表示数字,而这种格式无法准确地表示某些十进制小数

接下来写旋转的方法,然后给元素绑定点击事件就ok了

<script>export default {    methods: {        // 向左翻转    async turnLeftHandle() {      this.imgHandle.rotate = this.imgHandle.rotate - 90      await this.$nextTick()      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    // 向右翻转    async turnRightHandle() {      this.imgHandle.rotate = this.imgHandle.rotate + 90      await this.$nextTick()      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    }}</script>

最后记得给img图片加上过渡效果 transition: transform 0.3s ease; ,当控制图片操作的时候更平滑

最后的效果如下

组件代码

组件完整代码/components/PreviewImage/index.vue

<template>  <transition name="zoom">    <div v-if="show" class="previewImage_wrapper" @wheel="handleScroll">      <div class="previewImage_image">        <img ref="previewImage_img" :src="previewImgList[currentIndex] || ''">      </div>      <div class="previewImage_close previewImage_btn" @click="closePreviewImage">×</div>      <div class="previewImage_navigation">        <span class="previewImage_navigation_left previewImage_btn" @click="prevImage"><</span>        <span class="previewImage_navigation_right previewImage_btn" @click="nextImage">></span>      </div>      <div class="previewImage_toolbar">        <span class="previewImage_btn" @click="shrinkHandle">-</span>        <span class="previewImage_btn" @click="largeHandle">+</span>        <span class="previewImage_btn" @click="turnLeftHandle">↺</span>        <span class="previewImage_btn" @click="initImgHandle">▣</span>        <span class="previewImage_btn" @click="turnRightHandle">↻</span>      </div>    </div>  </transition></template><script>export default {  props: {    visible: { // 显示控制      type: Boolean,      default: false    },    previewImgList: { // url数组      type: Array,      default: () => []    },    currentIndex: { // 当前图片索引      type: Number,      default: 0    }  },  data() {    return {      imgHandle: { // 图片控制        scale: 1,        rotate: 0      }    }  },  computed: {    // 双向绑定    show: {      get() {        return this.visible      },      set(newVal) {        this.$emit('update:visible', newVal)      }    }  },  watch: {    visible: { // 给body动态增加style属性,禁止背景内容的鼠标滚轮滚动      handler(newVal) {        if(newVal) {          document.body.style.overflow = "hidden";          this.initImgHandle() // 每次打开图片初始化        } else {          document.body.style.overflow = "";        }      }    },  },  methods: {    // 鼠标滚轮    handleScroll(event) {      if (event.deltaY > 0) {        // 向下滚动事件        // console.log('向下滚动');        this.shrinkHandle()      } else {        // 向上滚动事件        // console.log('向上滚动');        this.largeHandle()      }    },    // 向左翻转    async turnLeftHandle() {      this.imgHandle.rotate = this.imgHandle.rotate - 90      await this.$nextTick()      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    // 向右翻转    async turnRightHandle() {      this.imgHandle.rotate = this.imgHandle.rotate + 90      await this.$nextTick()      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    // 初始化还原图片缩放旋转控制    async initImgHandle() {      this.imgHandle = {        scale: 1,        rotate: 0      }      await this.$nextTick()      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    // 放大图片    async largeHandle() {      console.log(this.imgHandle.scale, 'scale')      this.imgHandle.scale = Number((this.imgHandle.scale + 0.2).toFixed(2)) // 使用toFixed防止小数点精度不准      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    // 缩小图片    async shrinkHandle() {      console.log(this.imgHandle.scale, 'scale')      if (this.imgHandle.scale === 0.2) { // 最低缩放到0.2倍        return      }      this.imgHandle.scale = Number((this.imgHandle.scale - 0.2).toFixed(2)) // 使用toFixed防止小数点精度不准      const element = this.$refs.previewImage_img      element.style.transform = `scale(${this.imgHandle.scale}) rotate(${this.imgHandle.rotate}deg)`    },    // 上一张图片    prevImage() {      if (this.currentIndex === 0) {        this.currentIndex = this.previewImgList.length - 1      } else {        this.currentIndex--      }      this.initImgHandle()    },    // 下一张图片    nextImage() {      if (this.currentIndex === this.previewImgList.length - 1) {        this.currentIndex = 0      } else {        this.currentIndex++      }      this.initImgHandle()    },    // 关闭预览图片组件    closePreviewImage() {      this.show = false    }  },  mounted() { // 插入body    document.body.appendChild(this.$el);  },  destroyed() { // 组件销毁后同步清除元素    this.$el.parentNode.removeChild(this.$el);  }}</script><style lang="less" scoped>.previewImage_wrapper{  position: fixed;  top: 0;  right: 0;  bottom: 0;  left: 0;  background: rgba(0, 0, 0, .5);  z-index: 9999;  .previewImage_image{    display: flex;    align-items: center;    justify-content: center;    img {      width: 100vw;      height: 100vh;      object-fit: scale-down;      transition: transform 0.3s ease;     }  }  .previewImage_close{    position: absolute;    right: 20px;    top: 20px;    transition: transform 0.2s ease-out;    &:hover{      transform: scale(1.2);    }  }  .previewImage_navigation{    &_left{      position: absolute;      left: 15px;      top: 50%;      transform: translate(0, -50%);      transition: transform 0.2s ease-out;    }    &_right{      position: absolute;      right: 15px;      top: 50%;      transform: translate(0, -50%);      transition: transform 0.2s ease-out;    }    &_left:hover,&_right:hover{      transform: translate(0, -50%) scale(1.2);    }  }  .previewImage_toolbar{    position: absolute;    bottom: 10px;    left: 50%;    transform: translate(-50%, 0);    display: flex;    align-items: center;    span{      margin-right: 10px;      transition: transform 0.2s ease-out;      &:hover{        transform: scale(1.1) ;      }    }    span:last-child{      margin-right: 0;    }  }  .previewImage_btn{    width: 50px;    height: 50px;    display: flex;    align-items: center;    justify-content: center;    font-size: 24px;    color: #fff;    background-color: #606266;    border-radius: 50%;    cursor: pointer;  }}.zoom-enter, .zoom-leave-to { // 元素进入和离开时的动作  transform: scale(0);}.zoom-enter-active, .zoom-leave-active { // 元素进入和离开时的过渡动画定义  transition: transform 0.3s;}.slide-enter, .slide-leave-to { // 元素进入和离开时的动作  transform: translateX(100%);}.slide-enter-active, .slide-leave-active { // 元素进入和离开时的过渡动画定义  transition: transform 0.3s ease-in-out;}</style>  

使用组件代码

<template>  <div class="home">    <div class="home-box">      <img v-for="(item, index) in previewImg.imgUrlList" :key="item" :src="item" @click="openPreviewImg(index)">    </div>    <PreviewImage      :visible.sync="previewImg.visible"      :currentIndex="previewImg.currentIndex"      :previewImgList="previewImg.imgUrlList"    />  </div></template><script>import PreviewImage from '../components/PreviewImage/index.vue'export default {  name: 'Home',  components: { PreviewImage },  data() {    return {      previewImg: {        visible: false,        currentIndex: -1,        imgUrlList: [';, ';, ';, ';],      }    };  },  methods: {    // 打开预览图片遮罩层    openPreviewImg(index) {      this.previewImg.currentIndex = index      this.previewImg.visible = true    }  }}</script><style lang="less">.home{  height: 3000px;  &-box{    display: flex;    justify-items: center;    img{      width: 150px;      height: 150px;      cursor: pointer;    }  }}</style>

标签: #vue 翻页效果