vue.js 3.2.20: 用photoswipe实现图片的浏览:增加切换动画和自动播放(photoswipe@4.1.3)

一,安装photoswipe

liuhongdi@lhdpc:/data/vue/swipe$ npm i photoswipe --save

up to date in 6s

15 packages are looking for funding
  run `npm fund` for details

另:photoswipe的官网:

https://photoswipe.com/

此demo项目的源码:

https://gitee.com/liuhongdi/swipe

说明:刘宏缔的架构森林是一个专注架构的博客,

网站:https://blog.imgtouch.com
本文: https://blog.imgtouch.com/index.php/2023/05/28/vue-js-3-2-20-yong-photoswipe-shi-xian-tu-pian-de-liu-lan/

         对应的源码可以访问这里获取: https://github.com/liuhongdi/
         或: https://gitee.com/liuhongdi

说明:作者:刘宏缔 邮箱: 371125307@qq.com 

二,编写代码:

1,components/PhotoSwipe.vue

说明:增加了播放图片的功能

<template>
  <!-- 这段html代码,是用来显示图片弹出层的,是由photoswipe提供的 -->
  <!-- Root element of PhotoSwipe. Must have class pswp. -->
  <div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe.
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

      <!-- Container that holds slides.
          PhotoSwipe keeps only 3 of them in the DOM to save memory.
          Don't modify these 3 pswp__item elements, data is added later on. -->
      <div class="pswp__container">
        <div class="pswp__item"></div>
        <div class="pswp__item"></div>
        <div class="pswp__item"></div>
      </div>

      <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
      <div class="pswp__ui pswp__ui--hidden">

        <div class="pswp__top-bar">

          <!--  Controls are self-explanatory. Order can be changed. -->

          <div class="pswp__counter"></div>

          <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

          <button class="pswp__button pswp__button--share" title="Share"></button>

          <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

          <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>
          <!--
                    <button >放大</button>
                    <button >缩小</button>
                    <button >复原</button>

                    <button class="pswp__button" title="play">play</button>
                    -->
          <div style="color:#ffffff;float:right;margin-right:16px;margin-top:14px;" @click="play">
              <img id="playbutton" src="/static/img/play.png" style="width:16px;"/>
          </div>
          <!-- Preloader demo https://codepen.io/dimsemenov/pen/yyBWoR -->
          <!-- element will get class pswp__preloader--active when preloader is running -->
          <div class="pswp__preloader">
            <div class="pswp__preloader__icn">
              <div class="pswp__preloader__cut">
                <div class="pswp__preloader__donut"></div>
              </div>
            </div>
          </div>
        </div>

        <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
          <div class="pswp__share-tooltip"></div>
        </div>

        <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
        </button>

        <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
        </button>

        <div class="pswp__caption">
          <div class="pswp__caption__center"></div>
        </div>

      </div>

    </div>
  </div>
</template>
<script>
//import { getCurrentInstance } from 'vue'
// 引入photoswipe的js和css文件
import PhotoSwipe from 'photoswipe/dist/photoswipe'
import PhotoSwipeUI_Default from 'photoswipe/dist/photoswipe-ui-default'
import 'photoswipe/dist/photoswipe.css'
import 'photoswipe/dist/default-skin/default-skin.css'

export default {
  name: 'ZeroPhotoSwipe',
  data:function () {
    return {
       num:123,
       gallery:null,
       isPlaying:false,
       interval:null,
    }
  },
  methods: {

    //自动播放执行的内容
    autoplay:function() {
      let _self = this;
      _self.gallery.next();
    },

    //停止播放
    stop:function() {
      clearInterval(this.interval);
      this.isPlaying = false;
      this.cleartransition();
      document.getElementById('playbutton').src="/static/img/play.png";
    },

    //关闭transition
    cleartransition:function () {
      let containers = document.getElementsByClassName("pswp__container");
      let container = containers[0];
      container.style.transition = '';
    },

    //播放图片列表
    play:function() {
      if (this.isPlaying === true) {
          this.stop();
      } else {
          this.isPlaying = true;
          //console.log(this.gallery);
        document.getElementById('playbutton').src="/static/img/stop.png";
          this.interval = setInterval(this.autoplay,2000);
      }
    },

   //判断是否移动端的函数
    isMobileFunc:function() {
      let flag = navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i);
      if (flag === null) {
        return 0;
      } else {
        return 1;
      }
    },

    /**
     * 父组件调用初始化图片弹出层
     * photoswipe提供的方法,这里没有做任何修改,直接拿来用了
     * @param gallerySelector
     */
    initPhotoSwipeFromDOM:function(gallerySelector) {
      let _self = this;
      console.log('_self');
      console.log(_self);
      // parse slide data (url, title, size ...) from DOM elements
      // (children of gallerySelector)
      var parseThumbnailElements = function (el) {
        var thumbElements = el.childNodes,
            numNodes = thumbElements.length,
            items = [],
            figureEl,
            linkEl,
            //divEl,
            size,
            item;

        for (var i = 0; i < numNodes; i++) {

          figureEl = thumbElements[i]; // <figure> element

          // include only element nodes
          if (figureEl.nodeType !== 1) {
            continue;
          }

          linkEl = figureEl.children[0]; // <a> element
          //divEl = linkEl.children[0];



          console.log("linkEl:");
          console.log(linkEl);

          if(typeof(linkEl)=="undefined"){
            //alert("没有定义");
            //autoAddLine(formId);
            continue;
          }

          size = linkEl.getAttribute('data-size').split('x');

          console.log("size:w:"+size[0]);
          console.log("size:h:"+size[1]);

          let w = window.innerWidth;
          let dest_width = w;

           if (size[0] == 10 || size[1] == 10) {
             let imgEl = linkEl.children[0];
             let rect = imgEl.getBoundingClientRect();
             console.log(rect);

             let dest_height = dest_width * rect.height / rect.width;

             size[0] = dest_width;
             size[1] = dest_height;

           } else {

             let dest_height = dest_width * size[1] / size[0];

             size[0] = dest_width;
             size[1] = dest_height;
           }

          console.log('w:'+size[0]+";h:"+size[1]);
          item = {
            src: linkEl.getAttribute('href'),
            w: parseInt(size[0], 10),
            h: parseInt(size[1], 10)
          };

          if (figureEl.children.length > 1) {
            // <figcaption> content
            item.title = figureEl.children[1].innerHTML;
          }

          if (linkEl.children.length > 0) {
            // <img> thumbnail element, retrieving thumbnail url
            item.msrc = linkEl.children[0].getAttribute('src');
          }

          item.el = figureEl; // save link to element for getThumbBoundsFn
          items.push(item);
        }

        return items;
      };

      // find nearest parent element
      var closest = function closest(el, fn) {
        return el && ( fn(el) ? el : closest(el.parentNode, fn) );
      };

      // triggers when user clicks on thumbnail
      var onThumbnailsClick = function (e) {
        e = e || window.event;
        e.preventDefault ? e.preventDefault() : e.returnValue = false;

        var eTarget = e.target || e.srcElement;

        // find root element of slide
        var clickedListItem = closest(eTarget, function (el) {
          return (el.tagName && el.tagName.toUpperCase() === 'FIGURE');
        });

        if (!clickedListItem) {
          return;
        }

        // find index of clicked item by looping through all child nodes
        // alternatively, you may define index via data- attribute
        var clickedGallery = clickedListItem.parentNode,
            childNodes = clickedListItem.parentNode.childNodes,
            numChildNodes = childNodes.length,
            nodeIndex = 0,
            index;

        for (var i = 0; i < numChildNodes; i++) {
          //console.log("i:"+i);
          //console.log("nodeType:"+childNodes[i].nodeType);
          //console.log(childNodes[i].nodeName);
          if (childNodes[i].nodeType !== 1) {
            continue;
          }
          if (childNodes[i].nodeName !== 'FIGURE') {
            continue;
          }

          if (childNodes[i] === clickedListItem) {
            index = nodeIndex;
            break;
          }
          nodeIndex++;
        }


        if (index >= 0) {
          // open PhotoSwipe if valid index found
          //console.log('begin openPhotoSwipe 1');

          //const instance = getCurrentInstance()
          //const _this= instance.appContext.config.globalProperties;
          //console.log('_self:');
          //console.log(_self);
          _self.gallery = openPhotoSwipe(index, clickedGallery);
          //console.log('this gallery');
          //console.log(_self.gallery);

          _self.gallery.listen('destroy', function() {
            if (_self.isPlaying) {
                _self.stop();
            }
            //alert('is destroy');
          });

        }
        return false;
      };

      // parse picture index and gallery index from URL (#&pid=1&gid=2)
      var photoswipeParseHash = function () {
        var hash = window.location.hash.substring(1),
            params = {};

        if (hash.length < 5) {
          return params;
        }

        var vars = hash.split('&');
        for (var i = 0; i < vars.length; i++) {
          if (!vars[i]) {
            continue;
          }
          var pair = vars[i].split('=');
          if (pair.length < 2) {
            continue;
          }
          params[pair[0]] = pair[1];
        }

        if (params.gid) {
          params.gid = parseInt(params.gid, 10);
        }

        return params;
      };

      var openPhotoSwipe = function (index, galleryElement, disableAnimation, fromURL) {

        console.log('openPhotoSwipe');

        var pswpElement = document.querySelectorAll('.pswp')[0],
            gallery,
            options,
            items;

        items = parseThumbnailElements(galleryElement);

        /*
        *
            getThumbBoundsFn:function(){
                return {x:$self.left,y:$self.top,w:self[0].naturalWidth};
                //动画开始时从对应的图片放大到全屏,返回对应图片相对于窗口的实际坐标和自己的宽度
            },
            maxSpreadZoom:2.5,//手势放大图片最大倍数
            allowPanToNext:true,//图片处于放大状态是否允许滑动到下一张
            getDoubleTapZoom:function(){
                return 1;//双击后图片缩放到的倍数//1表示缩放到原始大小
            },
            loop:false,//滑动到最后一张是否可以继续循环到第一张
            history: false,
            focus: false,
            //closeOnVerticalDrag:false,//垂直拖动图片关闭弹层
            spacing:0.03,
            showAnimationDuration: 430,//显示大图动画时间
            hideAnimationDuration: 430,//隐藏大图动画时间
            showHideOpacity:true,//动画时淡出逐渐变透明
            index: index // 从哪一张图片开始
        * */


        // define options (if needed)
        options = {
          shareButtons: [
            {id:'wechat', label:'分享微信', url:'#'},
            {id:'weibo', label:'新浪微博', url:'#'},
            {id:'download', label:'保存图片', url:'{{raw_image_url}}', download:true}
          ],
          // define gallery index (for URL)
          galleryUID: galleryElement.getAttribute('data-pswp-uid'),
          maxSpreadZoom:2.5,
          getThumbBoundsFn: function (index) {
            // See Options -> getThumbBoundsFn section of documentation for more info
            var thumbnail = items[index].el.getElementsByTagName('img')[0], // find thumbnail
                pageYScroll = window.pageYOffset || document.documentElement.scrollTop,
                rect = thumbnail.getBoundingClientRect();

            return {x: rect.left, y: rect.top + pageYScroll, w: rect.width};
          }

        };

        // PhotoSwipe opened from URL
        if (fromURL) {
          if (options.galleryPIDs) {
            // parse real index when custom PIDs are used
            // http://photoswipe.com/documentation/faq.html#custom-pid-in-url
            for (var j = 0; j < items.length; j++) {
              if (items[j].pid == index) {
                options.index = j;
                break;
              }
            }
          } else {
            // in URL indexes start from 1
            options.index = parseInt(index, 10) - 1;
          }
        } else {
          options.index = parseInt(index, 10);
        }

        // exit if index not found
        if (isNaN(options.index)) {
          return;
        }

        if (disableAnimation) {
          options.showAnimationDuration = 0;
        }

        //options.
        // Pass data to PhotoSwipe and initialize it
        gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, options);
        gallery.init();
        return gallery;
      };

      // loop through all gallery elements and bind events
      var galleryElements = document.querySelectorAll(gallerySelector);

      for (var i = 0, l = galleryElements.length; i < l; i++) {
        galleryElements[i].setAttribute('data-pswp-uid', i + 1);
        galleryElements[i].onclick = onThumbnailsClick;
      }

      // Parse URL and open gallery if it contains #&pid=3&gid=1
      var hashData = photoswipeParseHash();
      if (hashData.pid && hashData.gid) {
        console.log('begin openPhotoSwipe 2');
        _self.gallery = openPhotoSwipe(hashData.pid, galleryElements[hashData.gid - 1], true, true);
        //this.gallery = gallery;

        _self.gallery.listen('destroy', function() {
          if (_self.isPlaying) {
            _self.stop();
          }
          //alert('is destroy');
        });

      }
    }
  },
}
</script>

 

2,components/Home.vue

<template>
  <div>
    <zero-photo-swipe ref="zeroPhoto"></zero-photo-swipe>
    <div class="my-gallery">

      <figure style="float:left;margin-inline-start:0px;margin-inline-end:0px;">
        <a href="/static/img/b.jpg" data-size="10x10">
          <img @mouseover="imgMouseOver(1)"  id="img1" src="/static/img/b.jpg"  style="max-width:100%;float:left;height:250px;border-radius: 5px;"  v-on:load="loadImgSize($event)"/>
          <div id="mask1" @mouseout="imgMouseOut(1)" style="display:none;width:100%;height:100%;background: #ffffff;opacity: 0.4;position: absolute;top:0px;left: 0px;">
              <img id="mag1" src="@/assets/img/mag.png" style="width:60px;height:60px;"/>
          </div>
        </a>
      </figure>
      <!--<div style="width:30px;height:100px;margin-left:30px;float:left;background: #ff0000;"></div>-->
      <figure style="float:left;margin-inline-start:0px;margin-inline-end:0px;">
        <a href="/static/img/c.jpg" data-size="10x10">
          <img @mouseover="imgMouseOver(2)"  id="img2" src="/static/img/c.jpg" style="max-width:100%;float:left;height:250px;margin-left: 20px;border-radius: 5px;"  v-on:load="loadImgSize($event)"/>
          <div id="mask2" @mouseout="imgMouseOut(2)" style="display:none;width:100%;height:100%;background: #ffffff;opacity: 0.4;position: absolute;top:0px;left: 0px;">
            <img id="mag2" src="@/assets/img/mag.png" style="width:60px;height:60px;"/>
          </div>
        </a>
      </figure>
      <figure style="float:left;margin-inline-start:0px;margin-inline-end:0px;">
        <a href="/static/img/tbh.jpeg" data-size="10x10">
          <img @mouseover="imgMouseOver(3)"  id="img3" src="/static/img/tbh.jpeg" style="max-width:100%;float:left;height:250px;margin-left: 20px;border-radius: 5px;"  v-on:load="loadImgSize($event)"/>
          <div id="mask3" @mouseout="imgMouseOut(3)" style="display:none;width:100%;height:100%;background: #ffffff;opacity: 0.4;position: absolute;top:0px;left: 0px;">
            <img id="mag3" src="@/assets/img/mag.png" style="width:60px;height:60px;"/>
          </div>
        </a>
      </figure>

      <figure style="float:left;margin-inline-start:0px;margin-inline-end:0px;">
        <a href="/static/img/qx.jpeg" data-size="10x10">
          <img @mouseover="imgMouseOver(4)"  id="img4" src="/static/img/qx.jpeg" style="max-width:100%;float:left;height:250px;margin-left: 20px;border-radius: 5px;"  v-on:load="loadImgSize($event)"/>
          <div id="mask4" @mouseout="imgMouseOut(4)" style="display:none;width:100%;height:100%;background: #ffffff;opacity: 0.4;position: absolute;top:0px;left: 0px;">
            <img id="mag4" src="@/assets/img/mag.png" style="width:60px;height:60px;"/>
          </div>
        </a>
      </figure>

      <figure style="float:left;margin-inline-start:0px;margin-inline-end:0px;">
        <a href="/static/img/lb.jpeg" data-size="10x10">
          <img @mouseover="imgMouseOver(5)"  id="img5" src="/static/img/lb.jpeg" style="max-width:100%;float:left;height:250px;margin-left: 20px;border-radius: 5px;"  v-on:load="loadImgSize($event)"/>
          <div id="mask5" @mouseout="imgMouseOut(5)" style="display:none;width:100%;height:100%;background: #ffffff;opacity: 0.4;position: absolute;top:0px;left: 0px;">
            <img id="mag5" src="@/assets/img/mag.png" style="width:60px;height:60px;"/>
          </div>
        </a>
      </figure>
      
      <figure style="float:left;margin-inline-start:0px;margin-inline-end:0px;">
        <a href="/static/img/gy.jpeg" data-size="10x10">
          <img @mouseover="imgMouseOver(6)"  id="img6" src="/static/img/gy.jpeg" style="max-width:100%;float:left;height:250px;margin-left: 20px;border-radius: 5px;"  v-on:load="loadImgSize($event)"/>
          <div id="mask6" @mouseout="imgMouseOut(6)" style="display:none;width:100%;height:100%;background: #ffffff;opacity: 0.4;position: absolute;top:0px;left: 0px;">
            <img id="mag6" src="@/assets/img/mag.png" style="width:60px;height:60px;"/>
          </div>
        </a>
      </figure>

    </div>
  </div>
</template>
<script>
import ZeroPhotoSwipe from "../components/PhotoSwipe.vue";
export default {
  name: 'Gallery',
  components: {
    ZeroPhotoSwipe
  },

  mounted(){
    // 调用初始化图片photoswipe
    this.$refs.zeroPhoto.initPhotoSwipeFromDOM('.my-gallery')
  },
  methods:{
    /**
     * 加载图片结束时自动添加photoswipe需要的data-size
     * @param e
     */
    loadImgSize(e) {
      var target = e.currentTarget
      var x = target.clientWidth
      var y = target.clientHeight

      target.dataSize = `${x}x${y}`
      target.parentNode.setAttribute('data-size', `${x}x${y}`)
    },
    imgMouseOver(id) {
      //alert(id);
      //let idmask="mask"+id;
      let rect = document.getElementById("img"+id).getBoundingClientRect();
      console.log(rect);

      let mask = document.getElementById("mask"+id);
      mask.style.left = rect.left +'px';
      mask.style.top = rect.top +'px';
      mask.style.width = rect.width +'px';
      mask.style.height = rect.height +'px';
      //mask.style.lineHeight = rect.height +'px';

      let mag = document.getElementById("mag"+id);
      mag.style.marginTop = (rect.height-60)/2 +'px';
      mask.style.display = "";
    },
    imgMouseOut(id) {
      let mask = document.getElementById("mask"+id);
      mask.style.display = "none";
    },
  }
}
</script>

 

三,修改photoswipe.js的代码

说明:主要是为了切换图片时的动画效果:

只修改next()和prev()两个方法即可

    next: function() {
        console.log('next');
        let tf = self.container.style.transform;
        //设置目标位置
        let  arr1 = tf.split("(");
        let str1 = arr1[1];
        //console.log("str1:"+str1);
        let arr2 = str1.split("px");
        let curleft = arr2[0];

        let w = _slideSize.x;
        let dest_left = parseInt(curleft)-w;
        self.container.style.transform = 'translate3d(' + dest_left + 'px, 0px, 0px)';
        self.container.style.transition = 'all 0.5s ease-in-out';
        setTimeout(function(){
            console.log('setTimeout begin');
            self.container.style.transition = 'all 0s ease-in-out';
            self.goTo( _currentItemIndex + 1);
        },550);
    },
    prev: function() {
        console.log('prev');

        let tf = self.container.style.transform;
        //设置目标位置
        let  arr1 = tf.split("(");
        let str1 = arr1[1];
        //console.log("str1:"+str1);
        let arr2 = str1.split("px");
        let curleft = arr2[0];

        let w = _slideSize.x;
        console.log("curleft:"+curleft+";w:"+w);
        let dest_left = parseInt(curleft)+w;
        console.log("dest_left:"+dest_left);
        self.container.style.transform = 'translate3d(' + dest_left + 'px, 0px, 0px)';
        self.container.style.transition = 'all 0.5s ease-in-out';
        setTimeout(function(){
            console.log('setTimeout begin');
            self.container.style.transition = 'all 0s ease-in-out';
             self.goTo( _currentItemIndex - 1);
        },550);

    },

 

四,查看效果:

 

五,查看版本:

查看vue的版本:
liuhongdi@lhdpc:/data/vue/swipe$ npm list vue
swipe@0.1.0 /data/vue/swipe
├─┬ @vue/cli-plugin-babel@4.5.13
│ └─┬ @vue/babel-preset-app@4.5.13
│   └── vue@3.2.20 deduped
└─┬ vue@3.2.20
  └─┬ @vue/server-renderer@3.2.20
    └── vue@3.2.20 deduped

 

查看photoswipe的版本:
liuhongdi@lhdpc:/data/vue/swipe$ npm list photoswipe
swipe@0.1.0 /data/vue/swipe
├── photoswipe@4.1.3 

 

posted @ 2021-10-19 15:11  刘宏缔的架构森林  阅读(1233)  评论(0编辑  收藏  举报