通过使用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); // 保持数据关联性
    }
GitHub 加速计划 / vu / Vue.Draggable
19.97 K
2.89 K
下载
SortableJS/Vue.Draggable: Vue.Draggable 是 Sortable.js 的 Vue.js 封装组件,提供了拖放排序功能,可以在 Vue 应用中轻松实现列表元素的可拖拽重排。
最近提交(Master分支:2 个月前 )
431db153 - 2 年前
017ab498 - 3 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐