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