vue手写图片预览


提示:本文是在vue2.0的项目创建的,同时使用了element ui的icon图标

文章目录

  • vue手写图片预览
  • 如何在vue项目中手写一个简单的图片预览组件
  • 前期
  • 子组件编写
  • 父组件调用
  • 总结

如何在vue项目中手写一个简单的图片预览组件


前期

我在网上找了挺久,但是没有找到几个满意的案例,于是决定自己手写了一个.
当然嫌麻烦的同学可以参考viewer这个插件
传送门:
提示:这篇文章是我随意找了一篇贴的,如有侵权请联系我删除:
在vue中使用viewer插件
viewer中文文档

子组件编写

以下的代码如有可以改进的地方,还希望大家在评论区里留言或私信告诉我,一起互相学习交流。

新建一个showImage.vue,代码如下:
1、项目使用到了element ui组件,例如文字提示框el-tooltip,图标等,如果直接cv引用代码,还要先安装element ui,这里贴一个传送门:element中文文档
2、要提一下的是,还使用到了 animate.css 动画,不了解的同学可以看看官方文档aniamte.css中文文档也可以看看我上一篇文章的第二点 —— 引入animate.css动画vue使用高德地图

<template><div style="position: relative; overflow: hidden"><!--    遮罩层--><div class="mark" style=""></div><!--    主体部分--><divclass="img_box animated":class="out ? 'zoomIn' : 'zoomOut'"@mousewheel="debounce(handleScroll($event), 200)"><!-- 右上角关闭按钮 --><div class="close" @click="closeMark"><i class="el-icon-close close_btn"></i></div><!--  图片  --><div style="margin: auto; position: relative"><imgwidth="100%"height="100%"class="img":style="imgStyle"@mousedown.prevent="dropImage":src="img"@keyup.left="handleActions('left')"@keyup.right="handleActions('right')"/></div><!--    上一张  --><div class="prev" @mouseover="isShow = true" @mouseleave="isShow = false"><iv-if="isShow"class="el-icon-arrow-left prev_img"@click="prevImg(2)"></i></div><!--   下一张--><div class="next" @mouseover="isShow = true" @mouseleave="isShow = false"><iv-if="isShow"class="el-icon-arrow-right next_img"@click="nextImg(2)"></i></div></div><!--    图片名字及页数--><div class="pages animated" :class="out ? 'flipInX' : 'zoomOutLeft'"><div class="image_name">{{ imageName }}</div><div>{{ nextIndex + 1 }} / {{ images.length }}</div></div><!--    底部操作区--><div class="img_footer animated" :class="out ? 'flipInX' : 'zoomOutLeft'"><div class="img_options"><el-tooltipcontent="图片比例1:1":open-delay="500"placement="bottom"effect="light"><i@click="handleActions('restore')"class="el-icon-c-scale-to-original"></i></el-tooltip><el-tooltipcontent="放大图片":open-delay="500"placement="bottom"effect="light"><i @click="handleActions('zoomIn')" class="el-icon-zoom-in"></i></el-tooltip><el-tooltipcontent="缩小图片":open-delay="500"placement="bottom"effect="light"><i @click="handleActions('zoomOut')" class="el-icon-zoom-out"></i></el-tooltip><el-tooltipcontent="逆时针90度":open-delay="500"placement="bottom"effect="light"><i@click="handleActions('refresh-left')"class="el-icon-refresh-left"></i></el-tooltip><el-tooltipcontent="顺时针90度":open-delay="500"placement="bottom"effect="light"><i@click="handleActions('refresh-right')"class="el-icon-refresh-right"></i></el-tooltip><el-tooltipcontent="第一张":open-delay="500"placement="bottom"effect="light"><i class="el-icon-d-arrow-left" @click="prevImg(1)"></i></el-tooltip><el-tooltipcontent="上一张":open-delay="500"placement="bottom"effect="light"><i class="el-icon-arrow-left" @click="prevImg(2)"></i></el-tooltip><el-tooltipcontent="下一张":open-delay="500"placement="bottom"effect="light"><i class="el-icon-arrow-right" @click="nextImg(2)"></i></el-tooltip><el-tooltipcontent="最后一张":open-delay="500"placement="bottom"effect="light"><i class="el-icon-d-arrow-right" @click="nextImg(1)"></i></el-tooltip><el-tooltipcontent="下载图片":open-delay="500"placement="bottom"effect="light"><i class="el-icon-download" @click="down"></i></el-tooltip><el-tooltipcontent="关闭预览":open-delay="500"placement="bottom"effect="light"><i class="el-icon-circle-close" @click="closeMark"></i></el-tooltip></div></div></div>
</template><script>
export default {props: ["images"],data() {return {out: true,img: "", // 图片路径odiv: null,transform: {scale: 1,degree: 0,},nextIndex: 0, //当前下标imageName: "", // 图片名字isShow: false,};},created() {if (this.images && this.images.length > 0) {this.imageName = this.images[this.nextIndex];this.img = this.images[this.nextIndex];}},computed: {// 是否是第一张isFirst() {return this.nextIndex === 0;},// 是否是最后一张isLast() {return this.nextIndex === this.images.length - 1;},// 用于计算图片缩小放大样式imgStyle() {const { scale, degree } = this.transform;const style = {transform: `scale(${scale}) rotate(${degree}deg)`,};return style;},},methods: {nextImg(type) {// 点击切换下一张if (this.images.length && this.images.length > 1) {if (this.isLast) {this.$message("这是最后一张啦");} else {if (type === 2) {this.nextIndex += 1;this.img = this.images[this.nextIndex];this.imageName = this.images[this.nextIndex];} else {this.nextIndex = this.images.length - 1;this.img = this.images[this.nextIndex];this.imageName = this.images[this.nextIndex];}this.reset();}} else {this.$message({type: "error",message: "错误预览!",});}},prevImg(type) {// 点击切换上一张if (this.images.length && this.images.length > 1) {if (this.isFirst) {this.$message("这就是第一张啦");} else {if (type === 2) {this.nextIndex -= 1;this.img = this.images[this.nextIndex];this.imageName = this.images[this.nextIndex];} else {this.nextIndex = 0;this.img = this.images[this.nextIndex];this.imageName = this.images[this.nextIndex];}this.reset();}} else {this.$message({type: "error",message: "错误预览!",});}},// 防抖,需要的自己调用以下 fn是要执行的函数,delay是延迟时间默认500msdebounce(fn, delay = 500) {// 是闭包中的let timer;// 事件调用的函数,相当于obj调用函数 return function () {// 这个if 判断不做也没关系,可直接清空,只有第一次timer非空if (timer) {clearTimeout(timer);}// 此时的箭头函数的this 和 arguments 都是从外部函数继承而来// r如果用普通函数就要用词法作用域 var thsta = this var arg = argumentstimer = setTimeout(() => {// 使得传入的回调函数的this 指向Input这个元素对象// arguments是该事件的详情,可以获得该函数被调用时的所有参数,是一个event 对象(所有Dom事件都会传event对象进入)// 直接使用 fn() 问题也不大fn.apply(this, arguments);timer = null;}, delay);};},reset() {// 初始化图片样式this.transform = {scale: 1,degree: 0,};},// 拖拽图片dropImage(e) {if (e === null) {return;}this.odiv = e.target; //获取目标元素//算出鼠标相对元素的位置let disX = e.clientX - this.odiv.offsetLeft;let disY = e.clientY - this.odiv.offsetTop;document.onmousemove = (s) => {//鼠标按下并移动的事件//用鼠标的位置减去鼠标相对元素的位置,得到元素的位置let left = s.clientX - disX;let top = s.clientY - disY;//移动当前元素this.odiv.style.left = left + "px";this.odiv.style.top = top + "px";};document.onmouseup = () => {document.onmousemove = null;document.onmouseup = null;};},// 关闭预览closeMark() {this.out = false;setTimeout(() => this.$emit("listenChild", false), 1000); // 这一秒延迟是为了执行动画},down() {let url = this.images[this.nextIndex];this.download(url,name);},// 下载  这里是做了非同源跨域处理,关于下载待改进的地方还有很多,大家不要吝啬发言哇!download(imageSrc,imgName) {let image = new Image();// 解决跨域 Canvas 污染问题image.setAttribute("crossOrigin", "anonymous");image.onload = function () {let canvas = document.createElement("canvas");canvas.width = image.width;canvas.height = image.height;let context = canvas.getContext("2d");context.drawImage(image, 0, 0, image.width, image.height);let url = canvas.toDataURL("image/png"); //得到图片的base64编码数据let a = document.createElement("a"); // 生成一个a元素let event = new MouseEvent("click"); // 创建一个单击事件a.download = imgName || "photo"; // 设置图片名称a.href = url; // 将生成的URL设置为a.href属性a.dispatchEvent(event); // 触发a的单击事件};image.src = imageSrc;},//判断滚动缩放handleScroll(e) {let direction = e.deltaY > 0 ? "down" : "up"; //deltaY为正则滚轮向下,为负滚轮向上if (direction === "down" && e.deltaY >= 100) {//125为用户一次滚动鼠标的wheelDelta的值this.handleActions("zoomOut");}if (direction === "up" && e.deltaY <= -100) {this.handleActions("zoomIn");}},// 旋转放大缩小handleActions(action) {// 放大旋转等操作const { zoomRate, rotateDeg, enableTransition } = {zoomRate: 0.2,rotateDeg: 90,enableTransition: true,};const { transform } = this;switch (action) {case "zoomOut":if (transform.scale > 0.2) {transform.scale = parseFloat((transform.scale - zoomRate).toFixed(3));}break;case "zoomIn":if (transform.scale < 7) {transform.scale = parseFloat((transform.scale + zoomRate).toFixed(3));}break;case "refresh-left":transform.degree -= rotateDeg;break;case "refresh-right":transform.degree += rotateDeg;break;case "restore":// 恢复1:1transform.scale = 1;break;}transform.enableTransition = enableTransition;},},watch: {images(newVal) {this.img = newVal;},},
};
</script><style scoped>
.mark {z-index: 1000;position: fixed;background-color: rgba(3, 3, 3, 0.6);top: 0;left: 0;bottom: 0;right: 0;
}
.img_box {position: fixed;top: 0;left: 0;bottom: 0;right: 0;z-index: 1050;display: flex;
}
.img {position: relative;
}
.close {position: fixed;width: 60px;height: 60px;z-index: 1060;top: 0;right: 0;font-size: 0.3rem;color: rgba(255, 255, 255, 0.8);background-color: rgba(38, 35, 35, 0.8);border-radius: 0 0 0 100%;cursor: pointer;transition: all 0.5s;
}
.close:hover {width: 80px;height: 80px;font-size: 0.4rem;
}
.close_btn {position: fixed;top: 15px;right: 15px;
}
.img_footer {z-index: 1051;position: fixed;height: 1.1rem;width: 100%;background-color: #0a0a0a;bottom: 0;left: 0;
}
.img_options {width: 100%;height: 0.8rem;background-color: rgba(241, 241, 241, 0.25);display: flex;justify-content: center;align-items: center;font-size: 0.48rem;
}
.img_options i {margin: 0 0.2rem;transition: all 0.5s;
}
.img_options i:hover {color: #ffffff;font-size: 0.6rem;cursor: pointer;
}
.pages {z-index: 1050;position: fixed;bottom: 1.1rem;left: 0;height: 0.5rem;width: 100%;font-size: 0.3rem;color: #ffffff;display: flex;flex-direction: column;justify-content: center;align-items: center;
}
.image_name {font-size: 0.18rem;color: #ffffff;
}
.next,
.prev {position: fixed;width: 2.5rem;height: 100%;font-size: 0.625rem;z-index: 1051;display: flex;justify-content: center;align-items: center;
}
.next {right: 0;
}
.prev {left: 0;
}
.prev_img,
.next_img {color: #ffffff;border: 0.0375rem solid #ffffff;border-radius: 100%;transition: all 0.5s;opacity: 0.3;
}
.prev_img {left: 12%;
}
.next_img {right: 12%;
}
.prev_img:hover,
.next_img:hover {cursor: pointer;opacity: 1;
}
</style>

父组件调用

新建index.vue文件,代码如下:

<template><div><button @click="openShowImage">图片预览</button><showImg  v-if="isShow" @listenChild="listenChild" :images="images"></showImg></div>
</template>
<script>import showImg from "../../../../components/showImg";export default {components:{ showImg },data(){return {images: ["https://qny.samt.top/3ukysgdRM9PP43ET.jpg","https://qny.samt.top/2SN2472MG0wpji.jpg"],isShow: false}},methods:{// 监听子组件事件listenChild(e) {this.isShow = e;},openShowImage() {this.isShow = true;},}}
</script>

总结

提示:文章到这里就结束了:
以上只是我个人的写法,如有不妥之处还请谅解我这个前端小白,欢迎大家在评论区留言,交流探讨。
对了,后续可能还会继续改进封装,所以大家别走太远哦~ 建议关注+收藏以免迷路。

【vue手写图片预览组件】在vue2.0项目中手写图片预览组件,旋转、放大、滚动、下载等功能相关推荐

  1. java对office、pdf文档在线预览解析(融合进项目中)

    最近在项目中要做一个文档的预览,在网上搜了好多demo,都可以实现其功能,但是放在自己的项目中有点复杂. 先说明本人的开发环境(win7+tomcat7+maven+svn+myeclipse),接下 ...

  2. Vue项目中加载图片的坑

    Vue项目中加载图片时,遇到的坑 1.当直接在标签中,使用图片路径,此时可以正常显示. <img src='img_src' /> 正常显示 2.当img标签的src属性为变量时,且该变量 ...

  3. vue2.0桌面端框架_Element-UI组件库(Vue2.0桌面端组件库)V2.9.2 免费版

    Element-UI组件库(Vue2.0桌面端组件库)是一款很优秀好用的为开发者.设计师和产品经理推出的基于Vue 2.0的桌面端组件库软件.小编带来的这款Element-UI组件库功能强大全面,简单 ...

  4. 如何制作Vuejs组件,并且在项目中使用

    如何制作VueJS的组件,并且在项目中使用 第一步:搭建一个JS的框架,这里我习惯使用AMD规范 define(function(require, exports, module){} 然后可以在项目 ...

  5. Vue2.0+SVG实现音乐播放圆形进度条组件,传入实时百分比实现圆圈进度动画效果

    vue2.0+SVG实现音乐播放圆形进度条组件,传入实时百分比实现圆圈进度动画效果 需求分析: 类似于大多数音乐播放器中等mini播放器控制按钮,显示播放进度,实时更新进度. progress-cir ...

  6. vue设置html自动跳转路由器,vue2.0项目实现路由跳转的方法详解

    一.安装 1.安装路由vue-router: npm install vue-router vue项目的依赖文件node_modules存在vue-router依赖,说明安装成功 2.vue项目引入v ...

  7. 创建VUE项目,vue-cli2.0版本和3.0版本的区别,将vue2.0项目升级为vue3.0项目

    创建VUE项目,vue-cli2.0版本和3.0版本的区别,将vue2.0项目升级为vue3.0项目 使用vue-cli2.0版本创建vue项目 创建前的准备 开始创建 创建过程 项目正常创建 使用v ...

  8. 在项目中这样写代码的时候,请搭配红花油、跌打损伤酒一起使用

    前言 前几天,正巧赶上组里代码review,一下午下来,感觉整个人都血压拉满了.五花八门的代码让我不禁感叹,代码规范这条道路还是任重而道远- 那么今天就来给大家总结一波Java中的代码小技巧,熟练掌握 ...

  9. 基于 next.js + mdx 搭建组件库文档项目(二) -- mdx 控件封装实现组件的演示与 Props 列表

    说明 经过上阶段的配置虽然可以在项目中使用 mdx 语法 来创建页面了,但是我们的组件库有一些定制化的需求:交互式的组件演示.组件 Props 列表展示.这些功能如果可以通过封装来实现,会大大提升开发 ...

  10. vue 专题 vue2.0各大前端移动端ui框架组件展示

    Vue 专题 一个数据驱动的组件,为现代化的 Web 界面而生.具有可扩展的数据绑定机制,原生对象即模型,简洁明了的 API 组件化 UI 构建 多个轻量库搭配使用 请访问链接: https://ww ...

最新文章

  1. 走火入魔 | 暑期电子设计课程学生们的作品
  2. Apache—DBUtils框架开发学习实例
  3. objectid.go源码阅读
  4. xy坐标正负方向_【笛卡尔坐标系】
  5. linux c++开发_Linux/Windows下进行C/C++开发的差异
  6. 人事管理系统整站php,卡盟整站程序源码 php版
  7. [笔记]根据二代居名身份证号码判断一个人的性别
  8. java数组的定义(菜鸟教程)
  9. hive建表设置如果为null_hive建表语句
  10. 注意丨Pearson VUE身份证件政策要求
  11. 程序员要实现财富自由,“出海”这条路该怎么走?
  12. 华为云 云学院 白嫖
  13. 如何批量把下载QQ空间相册图片
  14. 塑料填充母粒行业调研报告 - 市场现状分析与发展前景预测
  15. emi滤波matlab,EMI滤波器的基本原理
  16. 免疫组库vdj的数据处理(TCR/BCR)
  17. ural 2032 Conspiracy Theory and Rebranding (数学水题)
  18. cherry键盘alt+tab快捷键失效
  19. 【虹科分享】数字化仪的示波器功能(1)——数字化仪哪些特性能够代替示波器?
  20. 一个Job运行失败导致数据库挂死

热门文章

  1. 计算机考研去航空专业,北京航空航天大学计算机专业考研方向有哪些?
  2. 2016北航计算机学院研究生院,北京航空航天大学2016年计算机考研分数线_北京航空航天大学考研复试分数线...
  3. 企微主页_企业微信名片对外怎么显示官网和小程序?
  4. 写给前端的区块链开发入门指南:零基础开发基于以太坊智能合约的 ICO DApp
  5. java xps生成_Java PDF/XPS转为Word/html /SVG、PDF和XPS互转
  6. 用python画图的好处_用Python绘图,感受编程之美
  7. mysql5.8安装
  8. 【软件】HP惠普打印机能打印不能用扫描软件解决办法
  9. ansys2017安装教程_abaqus2017安装教程 - 仿真模拟 - 小木虫 - 学术 科研 互动社区
  10. vscode如何设置大小写转换的快捷键