当前位置: 代码网 > it编程>前端脚本>Vue.js > vue3使用threejs实现3D卡片水平旋转效果的示例代码

vue3使用threejs实现3D卡片水平旋转效果的示例代码

2024年05月26日 Vue.js 我要评论
这个是根据three现有案例来模仿实现,[原网址](three.js css3d - periodic table (threejs.org)效果图:template部分 <div class

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

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com