JS仿照3D手辦模型展臺實(shí)現(xiàn)示例詳解
前言
前幾年實(shí)現(xiàn)的一個 demo 效果,今天翻出來整理下,給大家提供類似場景參考思路。
當(dāng)時的需求場景是需要 3D 展示手辦模型,但是因?yàn)榧追筋A(yù)算有限,問有沒有其他青春版(性價比)方案。
剛好那段時間在處理 lottie 動畫跳幀的問題,就提出了給模型拍個全身照,旋轉(zhuǎn)的時候逐幀播放達(dá)到模擬手辦模型旋轉(zhuǎn)的動畫效果。
眾所周知,幀率越高,單位時間內(nèi)圖像幀的個數(shù)就會越多,對應(yīng)動畫效果就會越流暢,當(dāng)然了,對應(yīng)需要準(zhǔn)備的模型素材也就越多。
效果預(yù)覽
代碼片段
為了省流,這里沒有預(yù)渲染圖片資源,??可能出現(xiàn)轉(zhuǎn)動太快,圖片未加載的情況,請??等待片刻
style
body { padding: 24px; } .model-box { box-shadow: 0 10px 40px #00000029; padding: 24px; margin: 12px 0; border-radius: 12px; h4 { margin: 12px 0; } span { font-size: 12px; } } .content-box { position: relative; width: 318px; height: 300px; border: 1px solid #2196f3; border-radius: 20px; margin: 0 auto; img { position: absolute; top: 50%; pointer-events: none; width: 100%; transform: translateY(-50%); } }
Script
<template> <div class="model-box"> <button @click="auto">切換為自動旋轉(zhuǎn)</button> <h4>模型展臺 旋轉(zhuǎn)狀態(tài):{{ autoPlay ? '自動' : '手動'}} <span>(左右滑動旋轉(zhuǎn)模型)</span></h4> <div class="content-box" ref="content"> <img v-for="(item, i) in modelImgs" v-show="i === activeIndex" :key="i" :src="item" /> </div> </div> </template> <script> class ModelBooth { constructor ({ el, event, total }) { this.$el = el this.$event = event this.data = { total: total, index: 0, x: 0, y: 0 } this.change = this.throttle(this.emitChange.bind(this), 50) } init () { this.addListener() } addListener () { this.$el.addEventListener('touchstart', (e) => { this.$event?.onStop() this.data.x = e.touches[0].pageX this.data.y = e.touches[0].pageY }, false) this.$el.addEventListener('touchmove', (e) => { this.$event?.onStop() const endx = e.changedTouches[0].pageX const endy = e.changedTouches[0].pageY const direction = this.calcDirection(this.data.x, this.data.y, endx, endy) switch (direction) { case 'left': e.preventDefault() this.change(false) break case 'right': e.preventDefault() this.change(true) break } }, false) } auto ({ index }) { this.data.index = index this.change(true) } throttle (fn, time){ let t1 = 0 return function () { let t2 = Date.now() if (t2 - t1 > time) { fn.apply(this, arguments) t1 = t2 } } } emitChange (type) { let nowIndex = this.data.index if (!type) { ++nowIndex } else { --nowIndex } const result = ((nowIndex % this.data.total) + this.data.total) % this.data.total this.data.index = nowIndex this.$event?.onChange(result) } calcDirection (startX, startY, endX, endY) { const angX = endX - startX const angY = endY - startY let result = '' // 消除噪音 if (Math.abs(angX) < 2 && Math.abs(angY) < 2) { return result } const baseAngle = 45 const angle = this.calcAngle(angX, angY) if (angle >= -(baseAngle * 3) && angle <= -baseAngle) { result = 'left' } else if (angle > baseAngle && angle < (baseAngle * 3)) { result = 'right' } else if ((angle >= (baseAngle * 3) && angle <= (baseAngle * 4)) || (angle >= -(baseAngle * 4) && angle < -(baseAngle * 3))) { result = 'up' } else if (angle >= -baseAngle && angle <= baseAngle) { result = 'down' } return result } calcAngle (x, y) { return Math.atan2(x, y) * 180 / Math.PI } } export default { data() { return { modelImgs: Array.from({ length: 30 }).map((v, i) => `https://hi-zhang.com/assets/zip/${i}.png`), activeIndex: 0, autoPlay: false, playTn: null } }, mounted () { this._modelBooth = new ModelBooth({ el: this.$refs.content, total: this.modelImgs.length, event: { onChange: (index) => { this.activeIndex = index }, onStop: () => { this.stop() } } }) this._modelBooth.init() }, methods: { stop () { this.autoPlay = false clearInterval(this.playTn) }, auto () { this.stop() this.autoPlay = true this.playTn = setInterval(() => { this._modelBooth.auto({ index: this.activeIndex }) }, 50) } } } </script>
核心科技
- 搭建舞臺
- 把抓拍的各個視角模型照片,渲染到舞臺上,然后堆疊到一起備用
- 同時段只展示一個視角的模型照片
- 旋轉(zhuǎn)時同步視角
相信大家已經(jīng)知道如何去實(shí)現(xiàn)這個需求了,那么就再簡單貼一下相關(guān)的核心問題處理
獲取旋轉(zhuǎn)角度
監(jiān)聽 touch 事件,通過開始、結(jié)束坐標(biāo)計(jì)算移動方向
this.$el.addEventListener('touchstart', (e) => { this.data.x = e.touches[0].pageX this.data.y = e.touches[0].pageY }, false) this.$el.addEventListener('touchmove', (e) => { const endx = e.changedTouches[0].pageX const endy = e.changedTouches[0].pageY const direction = this.calcDirection(this.data.x, this.data.y, endx, endy) switch (direction) { case 'left': e.preventDefault() this.change(false) break case 'right': e.preventDefault() this.change(true) break } }, false)
根據(jù)坐標(biāo)計(jì)算移動方向
calcDirection (startX, startY, endX, endY) { const angX = endX - startX const angY = endY - startY let result = '' // 消除噪音 if (Math.abs(angX) < 2 && Math.abs(angY) < 2) { return result } const baseAngle = 45 const angle = this.calcAngle(angX, angY) if (angle >= -(baseAngle * 3) && angle <= -baseAngle) { result = 'left' } else if (angle > baseAngle && angle < (baseAngle * 3)) { result = 'right' } else if ((angle >= (baseAngle * 3) && angle <= (baseAngle * 4)) || (angle >= -(baseAngle * 4) && angle < -(baseAngle * 3))) { result = 'up' } else if (angle >= -baseAngle && angle <= baseAngle) { result = 'down' } return result } calcAngle (x, y) { return Math.atan2(x, y) * 180 / Math.PI }
PC端支持 touch 事件
到這里你會發(fā)現(xiàn)PC端是不支持 touch 事件的,還好這件事不是你第一個發(fā)現(xiàn)的,在 VantUI 的官方示例中,有成熟的方案,相信細(xì)心的你一定注意到了。
感慨一下,輸出技術(shù)文章還挺難的,組織措辭的時間比編碼時間還長,這還是在原有的基礎(chǔ)上整理,從頭構(gòu)建一個技術(shù)點(diǎn)的難度又是 up++++ ,以上就是JS 實(shí)現(xiàn)偽3D手辦模型展臺示例詳解的詳細(xì)內(nèi)容,更多關(guān)于JS 偽3D手辦模型展臺的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS實(shí)現(xiàn)一個可以當(dāng)鏡子照的?Button
這篇文章主要介紹了JS實(shí)現(xiàn)一個可以當(dāng)鏡子照的?Button的方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03詳解微信小程序 通過控制CSS實(shí)現(xiàn)view隱藏與顯示
這篇文章主要介紹了微信小程序 通過控制CSS實(shí)現(xiàn)view隱藏與顯示的相關(guān)資料,需要的朋友可以參考下2017-05-05JavaScript?Broadcast?Channel?API使用學(xué)習(xí)
這篇文章主要為大家介紹了JavaScript的api學(xué)習(xí)之Broadcast?Channel?API使用方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05微信小程序 location API接口詳解及實(shí)例代碼
這篇文章主要介紹了微信小程序 location API接口相關(guān)資料,這里詳細(xì)介紹了location API接口并附簡單實(shí)例代碼,需要的朋友可以參考下2016-10-10