vue项目three.js&&vue-draggable-resizable组件实现图片添加热点

通过使用three.js&&vue-draggable-resizable组件实现图片添加热点,场景世界坐标和平面热点二维坐标互转

项目描述:当前项目需求是,场景图中可以手动添加热点,并且热点可以在场景图上拖拽改变位置。
实现逻辑:

npm install --save vue-draggable-resizable
main.js添加如下代码
import VueDraggableResizable from 'vue-draggable-resizable'
import 'vue-draggable-resizable/dist/VueDraggableResizable.css'
Vue.component('vue-draggable-resizable', VueDraggableResizable)

首先通过three.js渲染场景,几何体采用球几何体,纹理采用整张图片贴图,然后dom中引用vue-draggable-resizable组件渲染热点,场景的position:relative;热点position:absolute;代码如下:

<div id="divContainer">
              <!--场景展示容器start-->
              <div ref="container" class="vrImage" ></div>
              <vue-draggable-resizable
                v-for="(item, index) in hotspot"
                v-if="item.imageUrl"
                :key="index"
                :id="index"
                :x.sync="item.left"
                :y.sync="item.top"
                :min-width="50"
                :min-height="50"
                :parent="false"
                :grid="[1,1]"
                :active="index === dragActiveIndex"
                :prevent-deactivation="index === dragActiveIndex"
                class-name="dragging"
                class-name-active= "activeClass"
                class-name-handle= "activeHandle"
                @dragging="onDrag"
                @dragstop = "onDragStop"
                @activated="onActivated(index)"
              >
                <div class="iconDiv">
                  <span>{{item.name}}</span>
                  <el-image
                    :src="item.imageUrl"></el-image>
                </div>
              </vue-draggable-resizable>
              <!--场景展示容器end-->
            </div>
 <style lang="less">
 #divContainer {
    position: relative;
    overflow: hidden;
    .vrImage {
	    height: 593px;
	    border: 1px solid;
 	 }
 	 .dragging {
	    position: absolute;
	    top:0;
	    height: 80px !important;
	    width: 80px !important;
	    transform: translateX(-50%) translateY(-50%);
  	}
  	 .activeClass{
    border: 2px solid yellow;
    height: 80px !important;
    width: 80px !important;
    border-radius: 15px;
  }
  .activeHandle {
    border: 0;
  }
    }
  }
  </style>

如果使用过vue-draggable-resizable这个插件应该知道,改组件的x、y值是相对与父级div的,那父级div的原点是左上角的顶点,二维坐标为(0, 0);当相机旋转,场景世界坐标和平面二维坐标互转,这里参考这个demo:

http://www.wjceo.com/blog/threejs2/2018-06-25/175.html

平面热点转二维坐标,代码如下:

setSceneGet3DPos (x, y) {
      // 根据鼠标位置创建射线
      let containerWidth = this.$refs.container.offsetWidth;
      let containerHeight = this.$refs.container.offsetHeight;
      let mouse = new THREE.Vector3();
      x = (x / containerWidth) * 2 - 1;
      y = -(y / containerHeight) * 2 + 1;
      let newRay = new THREE.Raycaster();
      newRay.setFromCamera({x: x, y: y}, this.camera);
      // 通过射线与小球的交点获取新热点的位置
      let newHotPos = newRay.intersectObject(this.mesh)[0].point; // mesh为场景网格,采用球几何体
      newHotPos.setLength(this.radius - 10);
      mouse.x = newHotPos.x.toFixed(4);
      mouse.y = newHotPos.y.toFixed(4);
      mouse.z = newHotPos.z.toFixed(4);
      return mouse;
    },
    onDrag(x, y) {
      this.controls.enabled = false;
      let mouse = this.setSceneGet3DPos(x + 40, y + 40); // 40 为图片大小的一半,本场景图片大小为80x80
      this.group.children[this.dragActiveIndex].position.set(mouse.x, mouse.y, mouse.z);
    },
    onActivated(index) {
      this.dragActiveIndex = index;
      this.hotSpotIndex = this.dragActiveIndex; // 热点下标
    },
    onDragStop(x, y) {
      this.controls.enabled = true;
      let mouse = this.setSceneGet3DPos(x + 40, y + 40); // 40 为图片大小的一半,本场景图片大小为80x80
     this.scenesObj.scenes[this.sceneIndex].hotspot[this.dragActiveIndex].atv = mouse.x;
      this.scenesObj.scenes[this.sceneIndex].hotspot[this.dragActiveIndex].ath = mouse.y;
      this.scenesObj.scenes[this.sceneIndex].hotspot[this.dragActiveIndex].atz = mouse.z;
    }

球面坐标转平面坐标:
this.render(
this.hotSpotChange();
)

hotSpotChange() {
      // 获取到窗口的一半高度和一半宽度
      let container = this.$refs.container;
      if (this.group.children.length === 0) return;
      let halfWidth = container.offsetWidth / 2;
      let halfHeight = container.offsetHeight / 2;
      let scene = this.scenesObj.scenes[this.sceneIndex];
      this.group.children.forEach((item, index) => {
        let vector = item.position.clone().project(this.camera);
        let hotspot = scene.hotspot[index];
        hotspot.left = vector.x * halfWidth + halfWidth - 40; // 40 为图片大小的一半,本场景图片大小为80x80
        hotspot.top = -vector.y * halfHeight + halfHeight - 40;
        if (document.getElementById(index.toString()) !== null) {
          if (vector.z > 1) {
            document.getElementById(index.toString()).style.visibility = "hidden";
          } else {
            document.getElementById(index.toString()).style.visibility = "";
          }
        }
      });
      this.scenesObj.scenes.splice(this.sceneIndex, 1, scene); // 保持数据关联性
    }
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值