vue3使用threejs实现3D卡片水平旋转效果的示例代码
作者:vue_邮递员
这篇文章主要介绍了在vue3中使用threejs实现3D卡片水平旋转效果,文中通过代码示例讲解的非常详细,对大家的学习或工作有一定的帮助,需要的朋友可以参考下
这个是根据three现有案例来模仿实现,[原网址](three.js css3d - periodic table (threejs.org)
效果图:
template部分
<div class="content1" ref="containerRef"> <div id="container" style="background-color: transparent"></div> </div>
script部分
import { onMounted,onUnmounted } from 'vue'; import { useRouter } from 'vue-router'; import * as THREE from 'three'; import TWEEN from 'three/addons/libs/tween.module.js'; import { TrackballControls } from 'three/addons/controls/TrackballControls.js'; import { CSS3DRenderer, CSS3DObject } from 'three/addons/renderers/CSS3DRenderer.js'; import { getSiteList } from '@/api/periodictable'; import { detailsInfo } from '@/views/mapDetail/data'; const props = defineProps({ id: { type: Number, defaults: '', }, }); const autoRotate = ref(true); const router = useRouter(); const containerRef = ref(); let camera, scene, renderer; let controls; let objects = []; const targets = { table: [], sphere: [], helix: [], grid: [], tablelist: [] }; // 图形初始化 function init() { console.log('containerRef', containerRef.value.clientWidth, containerRef.value.clientHeight); camera = new THREE.PerspectiveCamera(40, containerRef.value.clientWidth / containerRef.value.clientHeight, 1, 10000); camera.position.z = 3000; scene = new THREE.Scene(); renderer = new CSS3DRenderer({ alpha: true }); renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight); document.getElementById('container').style.background = 'transparent'; document.getElementById('container').appendChild(renderer.domElement); controls = new TrackballControls(camera, renderer.domElement); controls.minDistance = 500; controls.maxDistance = 6000; controls.addEventListener('change', render); controls.noPan = true controls.mouseButtons = { LEFT: THREE.MOUSE.RIGHT, RIGHT: THREE.MOUSE.LEFT, }; getList({ type: 4, siteId: props.id }); window.addEventListener('resize', onWindowResize); animate(); } // 变换 function transform(targets, duration) { TWEEN.removeAll(); for (let i = 0; i < objects.length; i++) { const object = objects[i]; const target = targets[i]; new TWEEN.Tween(object.position) .to({ x: target.position.x, y: target.position.y, z: target.position.z }, Math.random() * duration + duration) .easing(TWEEN.Easing.Exponential.InOut) .start(); new TWEEN.Tween(object.rotation) .to({ x: target.rotation.x, y: target.rotation.y, z: target.rotation.z }, Math.random() * duration + duration) .easing(TWEEN.Easing.Exponential.InOut) .start(); } new TWEEN.Tween(this) .to({}, duration * 2) .onUpdate(render) .start(); controls.reset(); controls.noRotate = false; } //窗口监听 function onWindowResize() { console.log('2222'); camera.aspect = containerRef.value.clientWidth / containerRef.value.clientHeight; camera.updateProjectionMatrix(); renderer.setSize(containerRef.value.clientWidth, containerRef.value.clientHeight); render(); } // 图形刷新 function animate() { requestAnimationFrame(animate); if (autoRotate.value) { scene.rotation.y += 0.001; // 旋转速度 } objects.forEach((object) => { object.lookAt(camera.position); //卡片取消翻转 }); TWEEN.update(); controls.update(); render(); } // 图形渲染 function render() { renderer.render(scene, camera); } // 查找数据 async function getList(query) { const { site } = await getSiteList(query); console.log('site', site); helixRender(site); } // 圆圈形状 function helixRender(data) { scene.clear(); objects = []; targets.helix = []; targets.circle = []; Object.keys(detailsInfo).forEach((key, index) => { // 构建元素 const element = document.createElement('div'); element.className = 'element1'; element.style.backgroundColor = 'rgba(0,127,127,' + (Math.random() * 0.5 + 0.25) + ')'; element.onmousedown = function (e) { e.ctrlKey && getList({ type: 1, yearName: key }); }; // const number = document.createElement('div'); // number.className = 'number'; // number.textContent = index + 1; // element.appendChild(number); const symbol = document.createElement('div'); symbol.className = 'symbol1'; symbol.textContent = data[key] ; element.appendChild(symbol); const details = document.createElement('div'); details.className = 'details'; details.innerHTML = detailsInfo[key].name; element.appendChild(details); const objectCSS = new CSS3DObject(element); objectCSS.position.x = Math.random() * 4000 - 2000; objectCSS.position.y = Math.random() * 4000 - 2000; objectCSS.position.z = Math.random() * 4000 - 2000; scene.add(objectCSS); objects.push(objectCSS); }); const radius = 400; // 设置圆形布局的半径 const vector = new THREE.Vector3(20, 20, 20); for (let i = 0, l = objects.length; i < l; i++) { const phi = (i / l) * 2 * Math.PI; // 分配每个对象在圆上的角度 const object = new THREE.Object3D(); object.position.x = radius * Math.cos(phi); object.position.y = 0; object.position.z = radius * Math.sin(phi); // 设置对象朝向圆心 vector.x = object.position.x; vector.y = object.position.y; vector.z = object.position.z; object.lookAt(vector); targets.circle.push(object); } transform(targets.circle, 0); camera.position.z = 1100; } const setControls = (bool) => { controls.noZoom = bool; // 启用缩放功能 controls.noRotate = bool; }; onMounted(() => { setTimeout(() => { init(); animate(); },200); }); onUnmounted(()=>{ window.removeEventListener('resize', onWindowResize); }) defineExpose({ setControls, });
style
<style lang="less" scoped> .content1 { height: 600px; width: 1000px; background-color: transparent !important; position: absolute; top: -290px; left: -122px; } </style> <style lang="less" > a { color: #8ff; } #menu { position: absolute; bottom: 20px; width: 100%; text-align: center; } .element1 { width: 120px; height: 130px; box-shadow: 0 0 12px rgb(0 255 255 / 50%); border: 1px solid rgb(127 255 255 / 25%); font-family: Helvetica, sans-serif; text-align: center; line-height: normal; cursor: default; display: flex; align-items: center; justify-content: center; } .element1:hover { box-shadow: 0 0 12px rgb(0 255 255 / 75%); border: 1px solid rgb(127 255 255 / 75%); } .element1 .number { position: absolute; top: 20px; right: 20px; font-size: 12px; color: rgb(127 255 255 / 75%); } .element1 .symbol1 { // position: absolute; // top: 15px; // left: 0; // right: 0; font-size: 16px; padding: 0 10px; margin-bottom: 20px; // height: 70px; // border:1px solid red; font-weight: bold; color: rgb(255 255 255 / 75%); text-shadow: 0 0 10px rgb(0 255 255 / 95%); white-space: normal; overflow: hidden; text-overflow: ellipsis; display: -webkit-box; //将对象作为弹性伸缩盒子模型显示。 -webkit-box-orient: vertical; // 从上到下垂直排列子元素 -webkit-line-clamp: 3; //显示的行数 } .element1.grid { width: 160px; height: 180px; } .element1 .grid-symbol { position: absolute; top: 35px; padding: 0 2px; left: 0; right: 0; font-size: 30px; font-weight: bold; color: rgb(255 255 255 / 75%); text-shadow: 0 0 10px rgb(0 255 255 / 95%); display: -webkit-box; -webkit-box-orient: vertical; /* 垂直排列子元素 */ /* 限制在两行 */ -webkit-line-clamp: 2; overflow: hidden; text-overflow: ellipsis; white-space: normal; } .element1 .table-symbol { position: absolute; top: 35px; padding: 0 2px; left: 0; right: 0; font-size: 20px; font-weight: bold; color: rgb(255 255 255 / 75%); text-shadow: 0 0 10px rgb(0 255 255 / 95%); display: -webkit-box; -webkit-box-orient: vertical; /* 垂直排列子元素 */ /* 限制在两行 */ -webkit-line-clamp: 2; overflow: hidden; text-overflow: ellipsis; white-space: normal; } .element1.publishname { width: 400px; } .element1.imageUrl { width: 400px; height: 340px; } .publishname .table-symbol { font-size: 36px; } .imageUrl .table-symbol { top: 3px; bottom: 3px; } .table-symbol .table-img { height: 100%; width: 100%; } .element1 .years { position: absolute; left: 6px; top: 6px; font-size: 14px; color: rgb(127 255 255 / 75%); } .element1 .subsymbol { position: absolute; top: 96px; left: 0; right: 0; font-size: 10px; color: rgb(255 255 255 / 75%); text-shadow: 0 0 10px rgb(0 255 255 / 95%); } .element1 .details { position: absolute; bottom: 15px; left: 0; right: 0; font-size: 14px; color: rgb(127 255 255 / 75%); } .element1 .table-details { position: absolute; bottom: 16px; left: 0; right: 0; font-size: 16px; color: rgb(127 255 255 / 75%); overflow: hidden; text-overflow: ellipsis; } .grid-name { font-size: 40px; font-weight: bold; color: rgb(255 255 255 / 75%); text-shadow: 0 0 10px rgb(0 255 255 / 95%); background-color: rgb(0 127 127 / 59%); padding: 20px 30px; border-radius: 6px; position: relative; } .grid-name .level-num { position: absolute; border: 1px solid rgb(127 255 255 / 75%); background-color: rgb(0 127 127 / 59%); display: inline-block; font-size: 12px; padding: 2px 4px; border-radius: 4px; right: 1px; top: 1px; } .show-more { font-size: 22px; // font-weight: bold; color: rgb(255 255 255 / 75%); text-shadow: 0 0 10px rgb(0 255 255 / 95%); background-color: rgb(0 127 127 / 59%); padding: 10px 30px; border-radius: 6px; } </style> <style lang="less" scoped> .type-picker { position: absolute; bottom: 20px; left: 50%; margin-left: -52px; z-index: 99; :deep(.el-radio-button.is-active) { .el-radio-button__inner { background-color: rgb(88 88 88 / 80%); } } :deep(.el-radio-button__inner) { background-color: rgb(36 36 36 / 50%); border: none !important; color: rgb(255 255 255 / 90%); padding: 10px 14px; box-shadow: none; } :deep(.el-radio-button:first-child .el-radio-button__inner) { border-radius: 45px 0 0 45px; } :deep(.el-radio-button:last-child .el-radio-button__inner) { border-radius: 0 45px 45px 0; } } </style>
以上就是vue3使用threejs实现3D卡片水平旋转效果的示例代码的详细内容,更多关于vue3 threejs3D卡片水平旋转的资料请关注脚本之家其它相关文章!