这个是根据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卡片水平旋转的资料请关注代码网其它相关文章!
发表评论