JS仿照3D手辦模型展臺(tái)實(shí)現(xiàn)示例詳解
前言
前幾年實(shí)現(xiàn)的一個(gè) demo 效果,今天翻出來整理下,給大家提供類似場景參考思路。
當(dāng)時(shí)的需求場景是需要 3D 展示手辦模型,但是因?yàn)榧追筋A(yù)算有限,問有沒有其他青春版(性價(jià)比)方案。
剛好那段時(shí)間在處理 lottie 動(dòng)畫跳幀的問題,就提出了給模型拍個(gè)全身照,旋轉(zhuǎn)的時(shí)候逐幀播放達(dá)到模擬手辦模型旋轉(zhuǎn)的動(dòng)畫效果。
眾所周知,幀率越高,單位時(shí)間內(nèi)圖像幀的個(gè)數(shù)就會(huì)越多,對(duì)應(yīng)動(dòng)畫效果就會(huì)越流暢,當(dāng)然了,對(duì)應(yīng)需要準(zhǔn)備的模型素材也就越多。
效果預(yù)覽

代碼片段
為了省流,這里沒有預(yù)渲染圖片資源,??可能出現(xiàn)轉(zhuǎn)動(dòng)太快,圖片未加載的情況,請(qǐng)??等待片刻
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">切換為自動(dòng)旋轉(zhuǎn)</button>
<h4>模型展臺(tái) 旋轉(zhuǎn)狀態(tài):{{ autoPlay ? '自動(dòng)' : '手動(dòng)'}} <span>(左右滑動(dòng)旋轉(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>核心科技
- 搭建舞臺(tái)
- 把抓拍的各個(gè)視角模型照片,渲染到舞臺(tái)上,然后堆疊到一起備用
- 同時(shí)段只展示一個(gè)視角的模型照片
- 旋轉(zhuǎn)時(shí)同步視角
相信大家已經(jīng)知道如何去實(shí)現(xiàn)這個(gè)需求了,那么就再簡單貼一下相關(guān)的核心問題處理
獲取旋轉(zhuǎn)角度
監(jiān)聽 touch 事件,通過開始、結(jié)束坐標(biāo)計(jì)算移動(dòng)方向
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ì)算移動(dòng)方向
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 事件
到這里你會(huì)發(fā)現(xiàn)PC端是不支持 touch 事件的,還好這件事不是你第一個(gè)發(fā)現(xiàn)的,在 VantUI 的官方示例中,有成熟的方案,相信細(xì)心的你一定注意到了。

感慨一下,輸出技術(shù)文章還挺難的,組織措辭的時(shí)間比編碼時(shí)間還長,這還是在原有的基礎(chǔ)上整理,從頭構(gòu)建一個(gè)技術(shù)點(diǎn)的難度又是 up++++ ,以上就是JS 實(shí)現(xiàn)偽3D手辦模型展臺(tái)示例詳解的詳細(xì)內(nèi)容,更多關(guān)于JS 偽3D手辦模型展臺(tái)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS實(shí)現(xiàn)一個(gè)可以當(dāng)鏡子照的?Button
這篇文章主要介紹了JS實(shí)現(xiàn)一個(gè)可以當(dāng)鏡子照的?Button的方法示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03
詳解微信小程序 通過控制CSS實(shí)現(xiàn)view隱藏與顯示
這篇文章主要介紹了微信小程序 通過控制CSS實(shí)現(xiàn)view隱藏與顯示的相關(guān)資料,需要的朋友可以參考下2017-05-05
JavaScript?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

