Vue实现图片预览,侧边栏懒加载,不用任何插件,简单好用

这篇具有很好参考价值的文章主要介绍了Vue实现图片预览,侧边栏懒加载,不用任何插件,简单好用。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

实现样式

Vue实现图片预览,侧边栏懒加载,不用任何插件,简单好用,vue,组件,vue.js,前端,canvas,pdf

需求

实现PDF上传预览,并且不能下载

第一次实现:用vue-pdf,将上传的文件用base64传给前端展示
问题:

  1. 水印第一次加载有后面又没有了。
  2. 当上传大的pdf文件后,前端获取和渲染又长又慢,甚至不能用

修改实现模式

  1. 前端上传PDF,后端将PDF转化成一页一页的图片
  2. 前端根据page去获取一页一页的PDF图片,类似于百度文库

实现思路

配合后端实现思路

  1. 获取全部页数,先把侧边栏的元素画出来占个位置
  2. 获取已经看到的页数,没有默认1
  3. 渲染上次看到的页数,同时侧边栏滚动到相同的index位置,通过监听元素是否进入视口去获取base64图片
  4. 已经获取回来的图片不再去请求

主要重点难点是侧边栏懒加载定位等比例展示图片文章来源地址https://www.toymoban.com/news/detail-821538.html

 <div class="pdf-viewer">
   <div class="pdf-main">
     <canvas id="pdf-view"></canvas>
   </div>
   <div class="pdf-list" :class="{ collapse: collapse }">
     <div
       class="pdf-item"
       :class="{ active: currentPage === index }"
       v-for="index in pageTotalNum"
       :key="index"
       @click="changePage(index)"
       :data-index="index"
     >
       <img :src="imgList[index - 1]" alt="" />
     </div>
   </div>
 </div>

<script>
let observer = null;
export default {
  name: "PDFView",
  data() {
    return {
      currentPage: 1, //当前页数
      pageTotalNum: 1, //总页数
      imgList: [], //base64图片列表
      updateTimer: null
    };
  },
  watch: {
    /**
     * @description 监听当前页变化 滚动列表到顶部
     */
    currentPage() {
      this.$nextTick(() => {
        const activeEl = document.querySelector(".pdf-list .active");
        if (activeEl) {
          document.querySelector(".pdf-list").scrollTo({
            top: activeEl.offsetTop - 20,
            behavior: "smooth",
          });
          // 解决进来会请求当前页数 前面所有图片
          setTimeout(() => {
            if (observer) {
              observer.disconnect();
            }
            this.isEnter();
          }, 500);
        }
        // 切换页面 将查看区域滚动到最上面
        const mainEl = document.querySelector(".pdf-main");
        mainEl.scrollTo({
          top: 0,
        });
      });
    },
  },
  mounted() {
    this.getPageTotal();
  },
  beforeDestroy() {
    if (observer) {
      observer.disconnect();
    }
  },
  methods: {
    /**
     * @description 获取pdf总页数
     */
    getPageTotal() {
      const params = {
        id: this.$route.query.id,
      };
      apiGetViewPdfPageTotal(params).then((response) => {
        this.pageTotalNum = response.data;
        this.updateStudy(true);
      });
    },
    /**
     * @description 切换当前页
     */
    changePage(index) {
      this.currentPage = index;
      this.updateStudy();
      if (this.imgList[index - 1]) {
        this.drawImage(this.imgList[index - 1]);
      } else {
        this.getPdf();
      }
    },
    /**
     * @description 上一页
     */
    prePage() {
      let page = this.currentPage;
      if (page !== 1) {
        page = page > 1 ? page - 1 : this.pageTotalNum;
        this.currentPage = page;
        this.updateStudy();
        if (this.imgList[page - 1]) {
          this.drawImage(this.imgList[page - 1]);
        } else {
          this.getPdf();
        }
      }
    },
    /**
     * @description 下一页
     */
    nextPage() {
      let page = this.currentPage;
      if (page !== this.pageTotalNum) {
        page = page < this.pageTotalNum ? page + 1 : 1;
        this.currentPage = page;
        this.updateStudy();
        if (this.imgList[page - 1]) {
          this.drawImage(this.imgList[page - 1]);
        } else {
          this.getPdf();
        }
      }
    },
    /**
     * @description 更新学习 flag=true第一次进入
     */
    updateStudy(flag = false) {
      const params = {
        courseId: this.$route.query.id,
        pageRate: this.currentPage,
        flag,
        totalPageRate: this.pageTotalNum,
      };
      apiUpdateStudy(params)
        .then((response) => {
          this.currentPage = response.data.pageRate;
          if (flag) {
            this.updateTimer = setInterval(() => {
              this.updateStudy();
            }, 1000 * 10);
          }
          if (flag) {
            this.getPdf();
            // 解决第一页进来不请求的问题,一页大概能展示4-5张
            if (this.currentPage < 5) {
              this.isEnter();
            }
          }
        })
    },
    /**
     * @description 查看资料
     */
    getPdf() {
      const params = {
        id: this.$route.query.id,
        page: this.currentPage,
      };
      apiGetPdf(params).then((response) => {
        let base64 = "data:image/png;base64," + response.data;
        this.drawImage(base64);
      });
    },
    /**
     * @description 将base64图片 画到canvas上
     */
    drawImage(base64) {
      const canvas = document.getElementById("pdf-view");
      const context = canvas.getContext("2d");
      const image = new Image();
      image.src = base64;
      image.onload = () => {
        const proportion = image.width / image.height;
        // 获取style设置width:100% 的canvas宽度
        const canvasWidth = canvas.offsetWidth;
        // 图片宽度与canvas宽度比例
        const canvasWidthProportion = image.width / canvasWidth;
        // canvas宽度设置为宽度
        canvas.width = image.width;
        // 根据图片比例和宽度比例计算出canvas高度
        canvas.height = (canvasWidth / proportion) * canvasWidthProportion;
        context.drawImage(image, 0, 0);
      };
    },
    /**
     * @description 监听元素进入视口
     */
    isEnter() {
      observer = new IntersectionObserver((entries) => {
        entries.forEach((entry) => {
          const target = entry.target;
          const index = target.dataset.index;
          if (entry.isIntersecting) {
            if (!this.imgList[index - 1]) {
              this.getImgList(index);
            }
          } else {
            // console.log("元素离开视口", index);
          }
        });
      });
      this.$nextTick(() => {
      //将所有侧边栏的元素进行监听
        const els = document.querySelectorAll(".pdf-item");
        Array.from(els).forEach((el) => {
          observer.observe(el);
        });
      });
    },
    /**
     * @description 滚动获取图片
     */
    getImgList(index) {
      const params = {
        id: this.$route.query.id,
        page: index,
      };
      apiGetPdf(params).then((response) => {
        let base64 = "data:image/png;base64," + response.data;
        this.imgList[index - 1] = base64;
        // 解决请求回来页面没更新的问题
        this.$forceUpdate();
      });
    },
  },
};
</script>

<style lang="scss" scoped>
.pdf-container {
  width: 100%;
  height: 100%;
  color: #999;
}
.pdf-viewer {
  width: 100%;
  height: calc(100vh - 50px - 30px - 60px - 6px);
  position: relative;
  display: flex;
}
.pdf-list {
  width: 240px;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  padding: 20px;
  background: #000;
  box-sizing: border-box;
  // transition: all 0.3s ease-in-out;
  border-left: 1px solid #999;
  &::-webkit-scrollbar {
    width: 0px;
  }
  .pdf-item {
    height: 183px;
    min-height: 183px;
    display: inline-flex;
    justify-content: center;
    align-items: center;
    cursor: pointer;
    overflow: hidden;
    &:hover {
      ::v-deep img {
        transition: all 0.5s ease-in-out;
        transform: scale(1.1);
      }
    }
    &.active {
      box-shadow: 0px 0px 0px 4px #e6a23c;
    }
    &:not(:last-child) {
      margin-bottom: 10px;
    }
    img {
      pointer-events: none;
      width: 100%;
      // height: 100%;
    }
  }
  &.collapse {
    width: 0;
    padding: 0;
  }
}
.pdf-main {
  flex: 1;
  // width: 100%;
  // height: 100%;
  overflow-y: auto;
  background: #000;
  position: relative;
  padding: 10px 0;
  &::-webkit-scrollbar {
    width: 0px;
  }
}
.handle-btn {
  background: #000;
  display: flex;
  font-size: 12px;
  position: relative;
  height: 60px;
  padding: 0 6px;
  border-bottom: 1px solid #999;
  .right {
    width: 240px;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    font-size: 32px;
  }
  .main {
    flex: 1;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 32px;
    margin-left: 250px;
    .pagination {
      display: flex;
      align-items: center;
      margin: 0 10px;
      .pagination-info {
        font-size: 14px;
        margin: 0 8px;
      }
    }
    .zoom {
      display: flex;
      align-items: center;
      margin: 0 10px;
      .scale {
        font-size: 14px;
        margin: 0 8px;
      }
    }
  }
  .tips {
    color: #e6a23c;
    font-size: 12px;
  }
  .start-test {
    display: flex;
    align-items: center;
  }
  .time {
    position: absolute;
    left: 6px;
    top: 50%;
    transform: translateY(-50%);
    > span {
      display: inline-block;
      margin-left: 10px;
    }
  }
}
i {
  cursor: pointer;
  &:hover {
    color: #fff;
  }
}
#pdf-view {
  width: 100%;
  // height: 100%;
  padding: 10px;
}
</style>

到了这里,关于Vue实现图片预览,侧边栏懒加载,不用任何插件,简单好用的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • vue+vant+pdfh5:实现点击 pdf 预览所有图片

    1、pdfh5 gitee 2、安装 3、使用及实现

    2024年02月11日
    浏览(28)
  • vue 实现图片懒加载

    有些页面可能展示的是大量的图片,如果我们一次性加载所有图片就会浪费性能,影响用户体验,所以我们就会懒加载这些图片。即可视区域之外的图片不加载,随着页面的滚动,图片进入可视区域,则触发图片的加载显示。 优点:页面加载速度快,用户体验感更好且节省流

    2024年02月12日
    浏览(76)
  • ant-design-vue中upload上传图片、视频实现预览功能

    有没有小伙伴在使用ant-design-vue的upload组件时,发现api文档在图片预览功能的介绍寥寥无几,而且也没提供视频预览的demo,在实际开发中碰到相应的需求直挠头~~~~,别急,下面来给大家分享一个我自己封装的upload组件,符合需求可以直接在项目中放到组件目录调用。 templat

    2024年02月12日
    浏览(34)
  • Vue3实现图片懒加载

    通过第三方插件VueUse实现图片懒加载。 1、需要安装依赖 2、定义懒加载的插件 xxx/index.js 3、在main.js文件内注册指令 4、使用 注册好指令就可以在标签上使用,使用指令时需要加v-。 这样一个自定义全局指令实现图片懒加载就做好了。

    2024年04月10日
    浏览(35)
  • Vue3 实现文件预览 Word Excel pdf 图片 视频等格式 大全!!!!

    先上效果图    插件安装 先说 word 文件是docx-preview插件           excel文件是用 xlsx 插件     介绍后端返回的数据 因为在拦截器处 做了对数据的处理 最后你调接口拿到的数据是 一个对象 里面包含: url :  blob对象转换的用于访问 Blob 数据的临时链接。这个链接可以被用于

    2024年02月07日
    浏览(52)
  • Vue3实现图片懒加载及自定义懒加载指令

    大家好,我是南木元元,热衷分享有趣实用的文章。图片懒加载是一种常见性能优化的方式,它只去加载可视区域图片,而不是在网页加载完毕后就立即加载所有图片,能减少很多不必要的请求,极大的提升用户体验。 图片懒加载的 实现原理 :在图片没进入可视区域的时候

    2024年02月13日
    浏览(23)
  • vue实战--vue+elementUI实现多文件上传+预览(word/PDF/图片/docx/doc/xlxs/txt)

        最近在做vue2.0+element UI的项目中遇到了一个需求:需求是多个文件上传的同时实现文件的在线预览功能。需求图如下:     看到这个需求的时候,小栗脑袋一炸。并不知道该如何下手,之前的实践项目中也并没有遇到相似的功能。因此也废了一番功夫想要实现这样一个

    2024年01月23日
    浏览(53)
  • 整体认识和路由配置、基础数据渲染、热榜区域实现、图片预览组件封装、认识SKU组件、通用组件统一注册全局(详情页)【Vue3】

    整体业务认识 路由配置 准备组件模板 配置路由 绑定模板测试跳转 封装接口 获取数据渲染模版 思考:渲染模版时遇到对象的多层属性访问可能出现什么问题? 模块实现整体分析 结论:两块热榜相比, 结构一致,标题title和列表内容不同 渲染基础热榜数据 1- 准备模版 2- 封

    2024年02月15日
    浏览(34)
  • nodejs实现解析chm文件列表,无需转换为PDF文件格式,在线预览chm文件以及目录,不依赖任何网页端插件

    特性: 1、支持任意深度的chm文件解析 2、解析后内容结构转换为tree数据呈现 3、点击树节点可以在html实时查看数据  4、不依赖任何浏览器端插件,兼容性较好

    2024年02月13日
    浏览(39)
  • vue3和vite项目在scss中因为本地图片,不用加~

    看了很多文章说要加~,真的好坑哦,我的加了~反而出不来了: 304 Not Modified 所以需要去掉~: 直接引入就好:

    2024年01月25日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包