淘宝/微信小程序中使用threeJs渲染3D场景demo
在做淘宝小程序的项目的时候需要有用到3d场景,然后就想到使用threeJs来做渲染,但是threeJs依据的dom元素在小程序里面是没有的,故而需要和web端的threeJs不一样的文件,一个兼容的threeJs文件
淘宝/微信小程序中使用的threeJs下载地址
页面代码
html元素
<!-- 一个canvas元素,type类型设置为webgl 事件需要拖动添加拖拽事件 -->
<view class="init_cabvas">
<canvas id="myCanvas" class="my_canvas" type="webgl" onTouchStart="touchStart" onTouchMove="touchMove" onTouchEnd="touchEnd" />
</view>
JS代码
// 引入threeJS
import {createScopedThreejs} from './threejs';
//设置一些全局变量,可以在每个函数中使用这些变量
let THREE,canvas,scene,geometry,material, mesh,camera,renderer,width, height,k, touchSX, touchSY, objs=[], touchObj, isMove = false, initrotationX = 0, initrotationY = 0, loadCanvas = false;
Page({
data: {
THREE: {},
canvas: {},
showCanvas: true,
isHidden: true
},
onLoad() {
},
onShow () {
//在onShow事件里面通过my.createCanvas来获取canvas文件,并通过createScopedThreejs来获取THREE。
my.createCanvas({
id: 'myCanvas',
success: (canvass) => {
canvas = canvass
THREE = createScopedThreejs(canvass);
scene = new THREE.Scene();
//创建3D场景
this.craeteBox()
}
})
},
craeteBox () {
//创建21个box立方体对象
for (let i = 0; i < 21; i++) {
mesh = null
geometry = null
material = null
/**
* 创建网格模型,创建400、400、400的立方体
*/
geometry = new THREE.BoxGeometry(400, 400, 400); //创建一个立方体几何对象Geometry
material = new THREE.MeshLambertMaterial({
color: 0xffffff * Math.random(),
transparent: true,
opacity: 1
}); //材质对象Material
mesh = new THREE.Mesh(geometry, material); //网格模型对象Mesh
//21个立方体,一行7个,总共3行,设置每个box的x、y、z的position位置
let x = 800 * (i % 7) - 2400;
let y = 1000 - 1000 * Math.floor(i / 7);
let z = 0;
let deg1 = (i % 7) * 21.145
z = -Math.cos(Math.PI * (Math.abs(63.435 - deg1) / 180) ) * 2683 + 1200
mesh.position.x = x;
mesh.position.y = y;
mesh.position.z = z;
let deg = Math.PI * (63.435 - deg1) / 180
scene.add(mesh); //网格模型添加到场景中
}
/**
* 光源设置
*/
//点光源
var point = new THREE.PointLight(0xffffff);
point.position.set(0, 0, 2500); //点光源位置
scene.add(point); //点光源添加到场景中
//环境光
var ambient = new THREE.AmbientLight(0x444444);
scene.add(ambient);
//添加三维坐标
// var axes = new THREE.AxisHelper(2500);
// scene.add(axes);
/**
* 相机设置
*/
width = canvas.width; //窗口宽度
height = canvas.height; //窗口高度
camera = new THREE.PerspectiveCamera( 45, width / height, 1, 10000 );
camera.position.set(0, 0, 4000)
renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);//设置渲染区域尺寸
renderer.setClearColor(0xb9d3ff, 1); //设置背景颜色
function render() {
//渲染的时候相机位置从4000拉近到1200
let z = camera.position.z;
if (z <= 1200) {
z = 1200;
camera.position.set(0, 0, z)
renderer.render(scene,camera)
return
} else {
z = z - 100
}
camera.position.set(0, 0, z)
renderer.render(scene,camera);//执行渲染操作
//淘宝小程序暂不支持requestAnimationFrame直接调用,需要canvas.requestAnimationFrame来调用
canvas.requestAnimationFrame(render)
}
render();
},
/**
* canvas的点击事件
*/
touchStart (e) {
if (loadCanvas) {
return
}
isMove = false
touchObj = e
touchSX = e.changedTouches[0].x
touchSY = e.changedTouches[0].y
initrotationX = camera.rotation.x
initrotationY = camera.rotation.y
},
touchMove (e) {
if (loadCanvas) {
return
}
isMove = true
let x = e.changedTouches[0].x - touchSX
let y = e.changedTouches[0].y - touchSY
x = x > 140 ? 140 : x;
y = y > 140 ? 140 : y;
x = x < -140 ? -140 : x;
y = y < -140 ? -140 : y;
camera.rotation.y = initrotationY + Math.PI * (x / 10) / 180
camera.rotation.x = initrotationX + Math.PI * (y / 10) / 180
renderer.render(scene,camera);
},
touchEnd (e) {
if (loadCanvas) {
return
}
if (!isMove) {
loadCanvas = true
let intersects = getIntersects(touchObj)
if (intersects.length != 0 && intersects[intersects.length - 1].object instanceof THREE.Mesh) {
let selectObject = intersects[intersects.length - 1].object;
let initX = camera.position.x;
let initY = camera.position.y;
let initZ = camera.position.z;
let initlineZ = Math.abs(scene.children[3].position.z);
let posX = selectObject.position.x;
let posY = selectObject.position.y;
let posZ = selectObject.position.z;
let camRotx = camera.rotation.x;
let camRoty = camera.rotation.y;
renderCamera(initX, initY, posX, posY, initZ, initlineZ, posZ, false, 0, camRotx, camRoty)
} else {
let initX = camera.position.x;
let initY = camera.position.y;
let initZ = camera.position.z;
let posX = 0;
let posY = 0;
let posZ = 0;
let initlineZ = 1000;
let camRotx = camera.rotation.x;
let camRoty = camera.rotation.y;
renderCamera(initX, initY, posX, posY, initZ, initlineZ, posZ, false, 0, camRotx, camRoty)
}
// 获取与射线相交的对象数组
function getIntersects(event) {
var mouse = new THREE.Vector2();
//射线相交检测在不同的系统、不同的手机渲染的情况不一样,需要获取手机系统信息进行判断才行。
let SystemInfoSync = my.getSystemInfoSync()
let Cwidth = (canvas.width / 2 ) / (canvas.width / SystemInfoSync.windowWidth)
let Cheight = (canvas.height / 2 ) / (canvas.height / SystemInfoSync.windowHeight)
mouse.x = (event.changedTouches[0].x - (Cwidth)) / (Cwidth)
mouse.y = -(event.changedTouches[0].y - (Cheight)) / (Cheight)
var vector = new THREE.Vector3(mouse.x, mouse.y,0.5).unproject(camera);
var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize());
var intersects = raycaster.intersectObjects(scene.children, true);
return intersects
}
function changeMaterial(object) {
var material = new THREE.MeshLambertMaterial({
color: 0xffffff * Math.random(),
transparent: object.material.transparent ? false : true,
opacity: 0.8
});
object.material = material;
}
//渲染点击的元素,来进行移动,分成15步进行渲染。
function renderCamera (initX, initY, posX, posY, initZ, initlineZ, posZ, init, index, initRotX, initRotY) {
let x = camera.position.x;
let y = camera.position.y;
let z = camera.position.z;
let chaX = (posX - initX) / 15
let chaY = (posY - initY) / 15
let chaZ = (initZ - (initlineZ - Math.abs(posZ) + 200)) / 15
camera.position.set(x + chaX, y + chaY, z - chaZ);
let nowRotX = camera.rotation.x
let nowRotY = camera.rotation.y
let initRotXx = initRotX / 15
let initRotXy = initRotY / 15
if (init) {
initRotXx = (0 - initRotX) / 15
initRotXy = (0 - initRotY) / 15
} else {
initRotXx = (initRotX - 0) / 15
initRotXy = (initRotY - 0) / 15
}
camera.rotation.set(nowRotX - initRotXx, nowRotY - initRotXy, 0)
renderer.render(scene,camera);
++index
if (index >= 15) {
loadCanvas = false
return
} else {
canvas.requestAnimationFrame(function () {
renderCamera(initX, initY, posX, posY, initZ, initlineZ, posZ, init, index, initRotX, initRotY)
})
}
}
}
}
});
3D需求场景样式demo展示
THREE.Raycaster射线检测在小程序里面IOS和安卓机兼容
射线检测在小程序里面会根据不同的手机、不同的系统,分辨率和宽高比不一样,返回的点击检测结果也不一样,需要进行不同的手机来做兼容。