该系列是为了让看过Vue官方文件或学过Vue但是却不知道怎麽下手去重构现在有的网站而去规画的系列文章,在这边整理了许多我自己使用Vue重构很多网站的经验分享给读者们。
我们在开发平台的时候常常会有需要上传商品图片的需求,但是往往我们会需要确定我们上传的图片到底对不对,或是要做一些修改确认,这时候预览的功能就很重要,在以前,前端的预览功能会需要後端先把图片存起来後,在前端在显示出来,或是透过已经死去的 Flash 来达成预览的功能,但是当html5的崛起,又出现了可以透过 canvas 的方式来做预览的动作,但是相对来说 canvas 相对复杂一点,所以後来就出现了新的 API 可以帮我们完成前端预览的功能。
URL.createObjectURL()
: 可以将我们 input 所选取的 FIle 物件转换成 Blob 物件给浏览器读取。MDN 文件:https://developer.mozilla.org/zh-TW/docs/Web/API/URL/createObjectURL
我们先来看一下我们要完成的样子长什麽样
当我今天 click 画面上的 button 的时候,会选取我的图片,然後确认会把这些图片变成预览图放到画面上。
首先我们来看一下我们要怎麽做,我首先会需要一个 button
的物件以及 type 是 file 的 input 元件
。
<template>
<div>
<input
type="file"
class="upload"
name="imgUpload"
multiple="multiple"
/>
<button>上传照片</button>
</div>
</template>
这边要记得把 input 加上 multiple="multiple"
不然没法多选档案
然後把这个 input 物件给隐藏起来,因为毕竟原生的物件样式没有很好看,我这边用了很多种藏起来的方式 (笑。
.upload {
position: fixed;
top: -500px;
left: -500;
z-index: -100;
opacity: 0;
}
接下来才是重点,我们要点击 button 来触发 input 的 click 这个动作,才能开启系统的选档案视窗,所以我先新增一个 ref 变数来抓取 input 的实体。
<script>
import { ref } from "vue";
export default {
setup() {
// input DOM
const inputDOM = ref(null);
const fileChange = (e) => {
console.log(e.target.files);
};
const uploadImages = () => {
inputDOM.value.click();
};
return {
inputDOM,
fileChange,
uploadImages,
};
},
};
</script>
<template>
<div>
<input
ref="inputDOM"
type="file"
class="upload"
name="imgUpload"
multiple="multiple"
@change="fileChange"
/>
<button @click="uploadImages">上传照片</button>
</div>
</template>
当我点击 button 的时候去触发 input 的 click 函式,这样就可以开启系统的选取档案视窗,然後当我选取的档案後,它就会触发 change
事件,然後我们就可以取得的到我们 input 的 file 物件,长成这样。
可以拿到物件之後我们就可以开始转换成预览用的 Blob 物件。
因为我们的 file 专换成 Blob 是一个 input 与 output 的动作,而且可能会依照不同的需求转换的中间过程会需要不同的处理,所以这边我们就很适合把整个转换的动作逻辑包成 Composition API。
首先我要检查 URL 这个物件有没有 在window 之中,因为不同的浏览器可能名字不一样,这点很重要。
window.URL = window.URL || window.webkitURL;
在来我们要新增我们的 function
export function useFileUpdate() {
// 预览用档案
const previewMap = ref({});
// 初始化
const initData = () => {
previewMap.value = {};
};
// 选择多个档案
const setFile = async (file = []) => {
initData();
previewMap.value = useQueuePreview(file);
};
return { setFile, previewMap };
}
这个 Composition API 主要会丢出两个东西,一个是负责接收File档案转换的 function,一个是转换好的档案物件,让我们可以直接跑一个 v-for
render的物件,所以你看这边我 return 的两个东西出去。
setFile
这个 function 里面有两个东西,一个是 initData
函式,一个是 useQueuePreview
的 Composition API ,我们先来看 initData
函式,这是为了每一次在选取要预览的图片的时候,先去清空前一次选取的图片所需要做的,让整个上传的行为变得正常。
至於 useQueuePreview
呢 ? 因为我们传入的 File 物件是多张图片,所以它是一个阵列,所以我需要透过回圈,一张张的做转换,所以我在这个档案里面写了一个新的 Composition API,因为这个只会用在 useFileUpdate
里面,所以我就不拆出去了。
// 本地预览
function useQueuePreview(fileArr) {
// 多图多影片列表
const previewMap = {};
// 排序索引
let idx = 0;
for (const file of fileArr) {
const fileData = useImageFilePreview(file);
previewMap[idx] = fileData;
idx++;
}
return previewMap;
}
// 读取 image 资料
function useImageFilePreview(file) {
return window.URL.createObjectURL(file);
}
我跑了一个回圈,然後把资料格式重组成 Map 格式,至於为什麽不直接用成阵列就好,请参考前几个章节的内容,回圈的执行内容我又在另外的写了一个useImageFilePreview
函式去做处理,把每个细节的动作都另外拆开,我可以很清楚的知道每个转换的阶段做了什麽事情。
完整的 useFileUpdate.js
的 code
import { ref } from "vue";
window.URL = window.URL || window.webkitURL;
// 读取 image 资料
function useImageFilePreview(file) {
return window.URL.createObjectURL(file);
}
// 本地预览
function useQueuePreview(fileArr) {
// 多图多影片列表
const previewMap = {};
// 排序索引
let idx = 0;
for (const file of fileArr) {
const fileData = useImageFilePreview(file);
previewMap[idx] = fileData;
idx++;
}
return previewMap;
}
export function useFileUpdate() {
// 预览用档案
const previewMap = ref({});
// 初始化
const initData = () => {
previewMap.value = {};
};
// 选择多个档案
const setFile = async (file = []) => {
initData();
previewMap.value = useQueuePreview(file);
console.log(previewMap.value);
};
return { setFile, previewMap };
}
这样一来,我们就可以在使用 useFileUpdate 的地方取得转换成预览图的物件,直接使用。
我们来看一下使用了 setFile
後,取得 previewMap
,加一个 <img />
跑一下 v-for
吧
<script>
import { ref } from "vue";
import { useFileUpdate } from "./composition-api/useFileUpdate.js";
export default {
setup() {
const { setFile, previewMap } = useFileUpdate();
// input DOM
const inputDOM = ref(null);
const fileChange = (e) => {
console.log(e.target.files);
setFile(e.target.files);
};
const uploadImages = () => {
inputDOM.value.click();
};
return {
inputDOM,
fileChange,
uploadImages,
previewMap,
};
},
};
</script>
<template>
<div>
<input
ref="inputDOM"
type="file"
class="upload"
name="imgUpload"
multiple="multiple"
@change="fileChange"
/>
<button @click="uploadImages">上传照片</button>
</div>
<div
v-show="Object.values(previewMap).length !== 0"
class="img_box"
v-for="item in previewMap"
:key="item"
>
<img :src="item" alt="" />
</div>
</template>
codesandbox 完成范例 : https://codesandbox.io/s/vue3-upload-img-preview-c629o?file=/src/App.vue:0-938
老板或是客户的脑袋永远是你无法掌握的,我们虽然可以看到预览图了,但是图片不足的地方就会露出黑色,毕竟使用者上传图片的时候我们不能去限制它上传的图片大小,所以我们需要把现在的黑底变成这样有模糊的样子。
这样是看起来比较美观啦,只是今天的篇幅够长了,我想还是留一点到明天再说吧,那我们明天一起在来完成它,帮它加上模糊的效果。
Ps. 购买的时候请登入或注册该平台的会员,然後再使用下面连结进入网站点击「立即购课」,这样才可以让我获得更多的课程分润,还可以帮助我完成更多丰富的内容给各位。
我有开设了一堂专门针对Vue3从零开始教学的课程,如果你觉得不错的话,可以购买我课程来学习
https://hiskio.com/bundles/9WwPNYRpz?s=tc
那如果对於JS基础不熟的朋友,我也有开设JS的入门课程,可以参考这个课程
https://hiskio.com/bundles/b9Rovqy7z?s=tc
Mike 的 Youtube 频道
Mike的medium
MIke 的官方 line 帐号,好友搜寻 @mike_cheng
<<: Day09 - 使用PopupWindow显示搜寻结果
>>: [Day23]C# 鸡础观念- 物件导向(oop)~属性(Property)
一、前言 上一篇文章有稍微带到简单的SQL基本CRUD操作方式,但实际玩起来我觉得就和GIT一样...
资料结构的逻辑结构 集合 逻辑:资料元素(紫色球)除了属於相同集合之外没有其他关系 类似结构 书:封...
数位 I/O 视窗当然是要有数位讯号相关功能啦。 数位 I/O 功能 在 Supported Mod...
在这边我会一起讲解这一 part 里面的组件,由於 App Bar 的部分之前已先讲解这边就不再提及...
本文将向您说明修复随身碟无法读取,在磁碟管理中显示为No Media错误的详细步骤。如何修复USB在...