?? 點擊這里 獲取國慶頭像,通過 Vue3 + Vant(組件庫) + Vite 構建,
應用的流程
在做之前,先思考它的流程:
- 上傳頭像
- 選擇頭像模板
- 保存新頭像
進入應用放出節日祝福與簡單的使用介紹,
開始僅顯示一個上傳頭像按鈕,這樣做的好處是,直接固定第一步操作,如果將模板和保存按鈕顯示出來需要增加代碼做一些判斷,如果開始就將頭像模板與保存按鈕顯示,可能讓人無從下手,
上傳頭像后直接將模板與保存按鈕顯示出來,在代碼中默認選中今年最流行的模板,直接點擊保存按鈕將下載這個頭像,開屏也進行了選擇模板的提示,如果點擊一個頭像模板,將進行切換,通過點擊保存按鈕保存它,
最后,點擊保存顯示煙花影片,
代碼實作
頭像上傳
<div
:style="{ marginTop: fileList.length === 0 ? '220px' : '80px' }"
>
<Uploader
v-model="fileList"
preview-size="120px"
:deletable="true"
:max-count="1"
:after-read="afterRead"
:before-delete="handleDelete"
/>
</div>
開始,僅顯示一個上傳按鈕,但希望它出現在螢屏靠中間位置,便于點擊,通過 vue 樣式系結即可,
.uploader-wrap {
display: flex;
justify-content: center;
transition: 0.3s margin;
}
在樣式代碼中給它添加 transition(過渡效果),
const fileList = ref([])
const base64 = ref('')
const afterRead = file => buildBase64(file.file)
const buildBase64 = file => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => {
base64.value = https://www.cnblogs.com/guangzan/archive/2021/10/01/reader.result
}
}
const handleDelete = () => {
base64.value =''
fileList.value = https://www.cnblogs.com/guangzan/archive/2021/10/01/[]
}
在 JS 中,通過 afterRead 獲取上傳的檔案,并將它轉化為 Base64 編碼,以便在后面將它在頭像模板中再次顯示,通過 handleDelete 將 回應屬性 base64 與 fileList 的值清空,由于上傳按鈕位置通過 vue 樣式系結,當 fileList 為空時,上傳按鈕會再次回到螢屏靠中間位置,
模板串列
<div >
<div
v-if="fileList.length !== 0"
v-for="(num, index) in 6"
:id="`item-${num}`"
:key="index"
@click="handleSelect"
>
<img
:src="https://www.cnblogs.com/guangzan/archive/2021/10/01/base64"
alt="avatar"
v-show="base64 !== ''"
/>
<img
:src="https://www.cnblogs.com/guangzan/archive/2021/10/01/getImageUrl(num)"
alt="avatar"
/>
</div>
</div>
通過 v-if 控制串列顯示,給每個串列項一個唯一的 id,以便區分它們,
.preview-list {
display: grid;
grid-template-rows: repeat(2, 1fr);
grid-template-columns: repeat(3, 1fr);
row-gap: 14px;
column-gap: 8px;
margin: 40px 0 60px;
.preview-item {
justify-self: center;
// ...
img {
display: block;
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
}
}
}
在樣式中,直接通過 grid 生成一個兩行三列的布局,由于給圖片的設定寬度小于網格寬度,通過 justify-self: center 使每個項水平居中,通過定位將兩個 img 重疊顯示,如果使用兩個 div,并分別給它們添加背景圖片(Base64)配合 Vue3 CSS 樣式變數注入將非常香,但生成 canvas 后將變得模糊,
const getImageUrl = name => new URL(`./assets/${name}.png`, import.meta.url).href
const selectedId = ref('item-1')
const handleSelect = e => {
selectedId.value = https://www.cnblogs.com/guangzan/archive/2021/10/01/e.currentTarget.id
Toast.success('選擇成功')
}
在 JS 中,由于 vue 無法在動態系結 src 至本地檔案,在 webpack 中使用 require 動態引入檔案即可, 在 vite 中,通過 vite 特有的 import.meta.url 來獲取,原因是,可以通過動態系結到 "/src/assets/ ${name}.png" 來顯示出頭像模板,但這在生產環境中無法被加載(資產被打包到 dist 或其他目錄下,assets 不在 src 下了),如果是 src 靜態的,直接通過系結 "./assets/${name}.png" 即可, 開發環境輸出到瀏覽器時,被編譯為 "/src/assets/name.png",在生產環境也能被正確顯示,另外也可以通過將靜態頭像模板圖片放到根目錄下的 public 來解決,在 vite build 時,public 會被復制到輸出檔案夾(ourDir)下,
保存頭像
<Button
v-if="fileList.length !== 0"
:loading="downloading"
@click="handleSave"
type="primary"
loading-text="保存中..."
block
color="#FA9935"
>保存</Button
>
template 中,使用 vant 組件庫加載按鈕,通過點擊觸發 handleSave 函式保存頭像,
const downloading = ref(false)
const handleSave = () => {
if (fileList.value.length === 0) {
Toast.fail('請先上傳頭像')
return
}
downloading.value = https://www.cnblogs.com/guangzan/archive/2021/10/01/true
html2canvas(document.querySelector(`#${selectedId.value}`), {
scale: 4,
allowTaint: true,
dpi: window.devicePixelRatio * 2,
}).then(canvas => {
// downloadjs(canvas.toDataURL(),'頭像.png', 'image/png')
saveAs(canvas.toDataURL(), 'image.jpg')
// const dataUrl = canvas.toDataURL()
// const a = document.createElement('a')
// a.download = 'avatar.png'
// a.href = https://www.cnblogs.com/guangzan/archive/2021/10/01/dataUrl
// a.click()
downloading.value = false
Toast.success('保存成功')
showFireworks.value = https://www.cnblogs.com/guangzan/archive/2021/10/01/true
setTimeout(() => {
showFireworks.value = false
}, 3000)
})
}
在 handleSave 函式中,開始將 downloading 的值更新為 true,按鈕這時為 loading 狀態,通過 html2canvas.js 將選中的頭像模板轉為 canvas,在結果中通過 canvas.toDataURL() 獲取 canvas dataUrl,在通過 file-saver 將頭像保存,通過 HTML a[download] 與 download.js 都不能很好的處理移動端兼容問題,最后,就是顯示煙花彩蛋了,在煙花影片播放完畢再將它關閉,
煙花效果與開屏提示
以上就是主要代碼了,下面分別是煙花效果與開屏提示組件的代碼片段,如果你對它們感興趣可以通過點擊展開查看它們,
Blessing.vue
<script setup>
import { ref } from 'vue'
const show = ref(true)
</script>
<template>
<van-dialog
v-model:show="show"
title="Hi,國慶節快樂"
theme="round-button"
confirmButtonText="好"
>
<div >
<ol>
<li>?? 上傳頭像</li>
<li>?? 選擇一個模板</li>
<li>?? 保存新頭像</li>
</ol>
<p>——來自 <a href="https://www.cnblogs.com/guangzan/">@guangzan</a></p>
</div>
</van-dialog>
</template>
<style lang="scss">
.blessing {
padding: 20px;
h4 {
margin-bottom: 16px;
}
p {
margin: 4px 0;
font-size: 12px;
color: #666;
text-align: right;
}
ol {
margin-left: 20px;
list-style: auto;
li {
margin-bottom: 4px;
}
}
a {
color: #b8251b;
}
}
</style>
Congratulate.vue
<template>
<div >
<div >
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
</div>
</div>
</template>
<style scoped lang="scss">
.container {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
overflow: hidden;
}
.wrapper {
position: relative;
height: 100vh;
display: flex;
flex-wrap: wrap;
}
.logo {
display: flex;
justify-content: center;
margin: auto;
}
[class|='confetti'] {
position: absolute;
}
$colors: (#d13447, #ffbf00, #263672);
@for $i from 0 through 200 {
$w: random(8);
$l: random(100);
.confetti-#{$i} {
width: #{$w}px;
height: #{$w * 0.4}px;
background-color: nth($colors, random(3));
top: -10%;
left: unquote($l + '%');
opacity: random() + 0.5;
transform: rotate(#{random() * 360}deg);
animation: drop-#{$i} unquote(4 + random() + 's') unquote(random() + 's');
}
@keyframes drop-#{$i} {
100% {
top: 110%;
left: unquote($l + random(15) + '%');
}
}
}
</style>
鏈接
- Live DEMO
- Github 代碼倉庫
- Gitee 代碼倉庫
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/308409.html
標籤:其他
下一篇:Redux使用指南
