thress.js要显示一个3D模型的必备元素:scene,camera.light,renderer
<script src="https://cdn.bootcdn.net/ajax/libs/three.js/r127/three.min.js"></script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000);
camera.position.z = 5;
var aLight = new THREE.AmbientLight(0xffffff,1);
scene.add(aLight);
var renderer = new THREE.WebGLRenderer({ });
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
renderer.render(scene,camera);
<canvas id="webgl" canvas-id="webgl" class="webgl" type="webgl" disable-scroll="{{true}}"></canvas>
const { createScopedThreejs } = require("threejs-miniprogram");
var THREE, renderer, scene, camera, canvas;
function initThree(canvasId, callback) {
const query = wx.createSelectorQuery();
query
.select("#" + canvasId)
.node()
.exec((res) => {
canvas = res[0].node;
THREE = createScopedThreejs(canvas);
THREE.canvas = canvas;
initScene();
if (typeof callback === "function") {
callback(THREE);
}
});
}
function initScene() {
scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0xffffff, 1.5));
camera = new THREE.PerspectiveCamera( 75, canvas.width / canvas.height, 1, 1000 );
camera.position.set(0, 3, 5);
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, preserveDrawingBuffer: true, });
const systemInfo = wx.getSystemInfoSync();
if (systemInfo.system.indexOf("Android") !== -1) {
renderer.setPixelRatio(systemInfo.pixelRatio); // 安卓适配像素比
renderer.setSize(canvas.width, canvas.height);
} else {
const ratio = systemInfo.pixelRatio; // ios 适配像素比展示
const width = systemInfo.windowWidth;
const height = systemInfo.windowHeight;
let canvasWidth = width * ratio;
let canvasHeight = height * ratio;
renderer.setSize(canvasWidth, canvasHeight);
}
moveAnimate();
}
function moveAnimate() {
animateId = canvas.requestAnimationFrame(moveAnimate);
renderer.render(scene, camera);
}
const { THREE, WechatPlatform, GLTFLoader, OrbitControls } = require("threejs-127/index-127.js");
var renderer, scene, camera, canvas, platform, disposing = false, frameId, controls;
function initThree(canvasId, callback) {
const query = wx.createSelectorQuery();
query
.select("#" + canvasId)
.node()
.exec((res) => {
canvas = res[0].node;
platform = new WechatPlatform(canvas);
THREE.PLATFORM.set(platform);
initScene();
if (typeof callback === "function") {
callback(THREE, gltfLoader);
}
});
}
function initScene() {
scene = new THREE.Scene();
scene.add(new THREE.AmbientLight(0xffffff, 0.5));
camera = new THREE.PerspectiveCamera(75, canvas.width / canvas.height, 0.0001, 1000);
camera.position.set(0, 3, 5);
renderer = new THREE.WebGL1Renderer({ canvas, antialias: true, alpha: true });
renderer.outputEncoding = THREE.sRGBEncoding
renderer.setSize(canvas.width, canvas.height)
renderer.setPixelRatio(THREE.$window.devicePixelRatio);
disposing = false;
const render = () => {
if (!disposing) frameId = THREE.$requestAnimationFrame(render);
renderer.render(scene, camera);
}
render()
}
<script src="js/loaders/GLTFLoader.js"></script>
const gltfLoader = new GLTFLoader();
gltfLoader.load(modelUrl,function(gltf){
const model = gltf.scene;
model.position.set(0,-30,150);
model.scale.set(300,300,300);
model.rotation.set(0.3,0,0);
scene.add(model);
});
基本要素: 网格形状Geometry
,材质material
,纹理texture(贴在材质上的图片,可省略)
,网格Mesh
var boxGeo = new THREE.BoxGeometry(1,1,1);
var sphereGeo = new THREE.SphereGeometry(0.5,40,40);
var cylinderGeo = new THREE.CylinderGeometry(0.5,0.8,1,25);
var octGeo = new THREE.PlaneGeometry(1,1);
var textureLoader = new THREE.TextureLoader();
textureLoader.load('./imgs/1.jpg',function(texture){// 异步,
var mat = new THREE.MeshLambertMaterial({map:texture,side: THREE.DoubleSide});
var box = new THREE.Mesh(boxGeo,mat);
scene.add(box);
var sphere = new THREE.Mesh(sphereGeo,mat);
sphere.position.set(2,0,0);
scene.add(sphere);
var cylinder = new THREE.Mesh(cylinderGeo,mat);
cylinder.position.set(-2,0,0);
scene.add(cylinder);
var oct = new THREE.Mesh(octGeo,mat);
oct.position.set(-2,-2,0);
scene.add(oct)
});
// 纹理对象与图片加载器
var imageLoader = new THREE.ImageLoader();
imageLoader.load('./imgs/2.jpg',function(img){
var texture = new THREE.Texture(img);// texture.image的属性值就是一张图片
texture.needsUpdate = true;
var mat = new THREE.MeshLambertMaterial({map:texture});
var mesh = new THREE.Mesh(boxGeo,mat);
mesh.position.y = 2;
scene.add(mesh);
})
#coverVideo{
/* visibility: hidden; */
position: fixed;
/* left: 0px; */
left: -1000000px;
top:0px;
z-index: 10000;
width: 320px;
height: 240px;
background-color: pink;
}
<video id="coverVideo" loop controls webkit-playsinline="true" x-webkit-airplay="true"
playsinline="true" x5-video-player-type="h5" preload="auto"></video>
<canvas id="canvas"></canvas>
const coverVideoTarget = document.getElementById('coverVideo');
function setVideo(videoUrl){
coverVideoTarget.src = videoUrl;
coverVideoTarget.play();
const texture = new THREE.VideoTexture(coverVideoTarget);
var mat = new THREE.MeshBasicMaterial({map: texture,transparent:true});
const geo = new THREE.PlaneGeometry(400,240);
const coverVideo = new THREE.Mesh(geo, mat);
scene.add(coverVideo);
// 可使用coverVideoTarget.pause()暂停视频纹理的播放;
}
使用canvas纹理来实现视频纹理
const coverVideoTarget = document.getElementById('coverVideo');
function setVideo2(videoUrl){
coverVideoTarget.src = videoUrl;
let vWidth = 400, vHeight = 240;
coverVideoTarget.addEventListener('canplay', function () {
vWidth = this.videoWidth;
vHeight = this.videoHeight;
});
var canvas_process = document.createElement('canvas');
var context_process = canvas_process.getContext('2d');
const texture = new THREE.CanvasTexture(canvas_process);
function update() {
context_process.fillStyle = 'black';
context_process.fillRect(0, 0, vWidth, vHeight);
context_process.drawImage(coverVideoTarget, 0, 0, vWidth, vHeight);
texture.needsUpdate = true;
requestAnimationFrame(update);
}
update();
var mat = new THREE.MeshBasicMaterial({map: texture,transparent:true});
const geo = new THREE.PlaneGeometry(400,240);
coverVideo = new THREE.Mesh(geo, mat);
scene.add(coverVideo);
}
<canvas type="webgl" id="canvas"></canvas>
import ThreeSpritePlayer from '../../tsp';
const { THREE, WechatPlatform, GLTFLoader, OrbitControls } = require("threejs-miniprogram/index-127.js");
const tile = {
url: Array(3)
.fill(0)
.map((v, k) => `/imgs/output-${k}.png`),
col: 2,
row: 2,
total: 10,
fps: 16,
};
let disposing,frameId,platform;
function getNode(id, ctx, fields = { node: true, rect: true, size: true }) {
return new Promise(function (resolve) {
wx.createSelectorQuery().in(ctx).select(id).fields(fields).exec(resolve);
});
}
Page({
async onLoad() {
const canvas = (await getNode('#canvas', this))[0].node;
platform = new WechatPlatform(canvas);
THREE.PLATFORM.set(platform);
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, canvas.width / canvas.height, 0.1, 100 );
const textureLoader = new THREE.TextureLoader();
Promise.all(
tile.url.map(
url =>
new Promise((resolve, reject) => {
textureLoader.load(
url,
texture => resolve(texture),
undefined,
reject,
);
}),
),
).then(textures => {
const spritePlayer = new ThreeSpritePlayer( textures, tile.total, tile.row, tile.col, tile.fps, false, );
// 可以自行构建mesh
const material = new THREE.MeshBasicMaterial({
map: spritePlayer.texture,
transparent: false,
});
const boxGeo = new THREE.BoxGeometry(5,5,5);
const boxMesh = new THREE.Mesh(boxGeo, material);
boxMesh.position.set(0,0,-15);
boxMesh.rotation.set(0,0.5,0);
scene.add(boxMesh);
// spritePlayer.stop();
var renderer = new THREE.WebGL1Renderer({ canvas, antialias: true, alpha: true });
renderer.outputEncoding = THREE.sRGBEncoding
renderer.setSize(canvas.width, canvas.height)
renderer.setPixelRatio(THREE.$window.devicePixelRatio);
disposing = false;
const render = () => {
if (!disposing) frameId = THREE.$requestAnimationFrame(render);
spritePlayer.animate();
// 更新material.map
material.map = spritePlayer.texture;
renderer.render(scene, camera);
}
render()
});
}
});
var material = new THREE.MeshBasicMaterial(
{
map: textureLoader.load(texure_url),
opacity:0.99,
transparent: true
}
);//材料
设置模型的材质material.depthTest = false;
// data.json
[{"name":"seat_F2_01","state":"canReserve","position":{"x":0.343436778,"y":-0.010585973,"z":-0.156569347}},
{"name":"seat_F2_02","state":"canReserve","position":{"x":0.355292529,"y":-0.010585974,"z":-0.138982728}}]
var canTexture = new THREE.TextureLoader().load("./imgs/can.jpg");
var cannotTexture = new THREE.TextureLoader().load("./imgs/cannot.jpg");
let group = new THREE.Group();
var loader = new THREE.FileLoader().setResponseType('json');
gltfLoader.load(modelUrl,function(gltf){
const model = gltf.scene;
model.rotation.set(0.3,0,0);
scene.add(model);
loader.load('./js/data.json', function(data) {
data.forEach(elem => {
let texture = elem.state === 'canReserve' ? canTexture : cannotTexture;
var spriteMaterial = new THREE.SpriteMaterial({
map: texture, //设置精灵纹理贴图
transparent: true,
opacity: 0.5,
});
var sprite = new THREE.Sprite(spriteMaterial);
group.add(sprite);
sprite.scale.set(0.5, 0.5, 1);
sprite.position.set(elem.position.x, elem.position.y, elem.position.z)
});
group.position.set(0, 0.01, 0);
model.add(group);//把精灵群组插入场景中
})
// 用于遍历模型得到椅子的位置(只在开发阶段使用)
// var seatJson = [];
// model.traverse(item => {
// if (item.name.indexOf('seat') !== -1) {
// seatJson.push({ name: item.name, state: 'canReserve', position: item.position});
// }
// })
// console.log(JSON.stringify(seatJson));
});
// 创建一个明信片,明信片正反面贴图不一样
var box = new THREE.BoxGeometry(20,15,0.2);
var textureLoader = new THREE.TextureLoader();
let mat = makeMat(`https://houtaicdn.alva.com.cn/medias/resources/wechat/arread/chongqing/mingxinpian/card-scene1.jpg`);
var textMat = makeMat(`https://houtaicdn.alva.com.cn/medias/resources/wechat/arread/chongqing/mingxinpian/card-demo1.png`)
let card = makeCard();
scene.add(card)
function makeMat(imgUrl) {
var texture = textureLoader.load(imgUrl);
texture.minFilter = THREE.LinearFilter;
var mat = new THREE.MeshBasicMaterial({map: texture});
return mat;
}
function makeCard() {
var matArr = [mat,textMat];
box.faces[10].materialIndex = 1;
box.faces[11].materialIndex = 1;
box.faces[8].materialIndex = 0;
box.faces[9].materialIndex = 0;
// 如果box没有faces属性,看有没有faceVertexUvs属性,这两个属性均是用于设置哪个面对应哪张纹理的
var card = new THREE.Mesh(box, matArr);
return card;
}
function hudieTrans(model){
if (!model) return;
if(model.targetPosition === undefined){
model.targetPosition = new THREE.Vector3(model.position.x, model.position.y, model.position.z);
}
if (model.position.distanceTo(model.targetPosition) < 0.4) {
var targetPosition = model.targetPosition = new THREE.Vector3(randomNum(-50, 50), randomNum(-5, 5), randomNum(-50, 50));
var dirV3 = new THREE.Vector3();
dirV3.subVectors(model.targetPosition, model.position);
model.speedV3 = dirV3.normalize();
model.speedV3 = v3Scale(model.speedV3, 0.02);
model.lookAt( targetPosition.x, targetPosition.y, targetPosition.z);// 改变模型的方向
} else {
model.position.x += model.speedV3.x;
model.position.y += model.speedV3.y;
model.position.z += model.speedV3.z;
}
}
function v3Scale(v3,scale){
return new THREE.Vector3(v3.x * scale, v3.y * scale, v3.z * scale)
}
function randomNum(minNum, maxNum) {
switch (arguments.length) {
case 1:
return parseInt(Math.random() * minNum + 1, 10);
case 2:
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10);
default:
return 0;
}
}
<script src="js/OrbitControls.js"></script>
var controls = new THREE.OrbitControls(camera)
controls.enableZoom = true
//controls.autoRotate = true;
controls.minDistance = 10;
controls.maxDistance = 300;
controls.maxPolarAngle = 1.5;
controls.minPolarAngle = 1.5;
controls.enablePan = false;
animate()
function animate(){
controls.update()
requestAnimationFrame(animate);
renderer.render(scene,camera);
}
<canvas id="webgl" canvas-id="webgl" class="webgl" type="webgl" disable-scroll="{{true}}" bindtouchstart="onTX" bindtouchmove="onTX" bindtouchend="onTX"></canvas>
const { THREE, WechatPlatform, GLTFLoader, OrbitControls } = require("threejs-127/index-127.js");
controls = new OrbitControls(camera, canvas);
controls.enableDamping = true
const render = () => {
if (!disposing) frameId = THREE.$requestAnimationFrame(render);
controls.update();
renderer.render(scene, camera);
}
render()
onTX(e) {
platform.dispatchTouchEvent(e)
}
<canvas id="webgl" canvas-id="webgl" class="webgl" type="webgl" disable-scroll="{{true}}" bindtouchmove='ontouchmove' bindtouchend="ontouchend"></canvas>
let first = true,
isTwoTouch = false,
twoFirst = true,
lastPositon = {},
lastDistance,
lastPoint = {},
steps = 1;
ontouchmove(e) {
let touches = e.touches;
if (touches.length === 1) {
if (isTwoTouch) {
return;
}
if (first) {
lastPositon.x = touches[0].x;
lastPositon.y = touches[0].y;
first = false;
} else {
let deltaY = (touches[0].y - lastPositon.y) / 100;
let deltaX = (touches[0].x - lastPositon.x) / 100;
lastPositon.x = touches[0].x;
lastPositon.y = touches[0].y;
currModel.rotation.x += deltaY;
currModel.rotation.y += deltaX;
}
} else if (touches.length === 2) {
isTwoTouch = true;
let t1 = touches[0],
t2 = touches[1];
if (twoFirst) {
lastDistance = this.calDistance(t1, t2);
lastPoint = t1;
twoFirst = false;
} else {
let currDistance = this.calDistance(t1, t2);
let factor = 0.001 * (currDistance - lastDistance);
if (Math.abs(factor) < 0.001) {
// 移动
var disX = (touches[0].x - lastPoint.x) * 0.01;
var disY = -(touches[0].y - lastPoint.y) * 0.01;
currModel.position.x += disX;
currModel.position.y += disY;
} else {
currModel.scale.x += factor;
currModel.scale.y += factor;
currModel.scale.z += factor;
}
lastDistance = currDistance;
lastPoint.x = touches[0].x;
lastPoint.y = touches[0].y;
}
}
},
calDistance(t1, t2) {
let v1 = new THREE.Vector2(t1.x, t1.y);
let v2 = new THREE.Vector2(t2.x, t2.y);
return v1.distanceTo(v2);
},
ontouchend() {
first = true;
twoFirst = true;
lastPositon.x = 0;
lastPositon.y = 0;
lastPoint.x = 0;
lastPoint.y = 0;
isTwoTouch = false;
},
checkMatch() {
if (!currModel) return;
let { position, rotation, scale } = currModel;// 获取当前模型的当前状态值
let { cPosition, cRotation, cScale } = config[mIndex];// 获取规定数值
let p = new THREE.Vector3(position.x, position.y, position.z);
let cP = new THREE.Vector3(cPosition.x, cPosition.y, cPosition.z);
let r = new THREE.Vector3(rotation.x, rotation.y, rotation.z);
let cR = new THREE.Vector3(cRotation.x, cRotation.y, cRotation.z);
// console.log(p.distanceTo(cP).toFixed(2), r.angleTo(cR).toFixed(2), Math.abs(scale.x - cScale).toFixed(2));
// console.log((r.angleTo(cR)).toFixed(2))
// console.log(position, rotation, scale, "------");
if (p.distanceTo(cP) > 0.45) {// 0.45这个值为允许误差值
return "position not match";
}
if (r.angleTo(cR) > 0.45) {// 0.45这个值为允许误差值
return "angle not match";
}
if (Math.abs(scale.x - cScale) > 0.45) {// 0.45这个值为允许误差值
return "scale not match";
}
return "match";
},
// 绘制摇杆
function initRocker(){
let outerDiameter = 100;// 外圆直径
let innerDiameter = 35;// 内圆直径
let outerRadius = outerDiameter / 2;
let innerRadius = innerDiameter / 2;
let centerNum = (outerDiameter - innerDiameter) / 2;// 内圆位置
let rockerBox = document.createElement('div');
setStyle(rockerBox,{
width: `${outerDiameter}px`,
height: `${outerDiameter}px`,
borderRadius: `${outerRadius}px`,
position: 'fixed',
bottom: '2rem',
right: '4rem',
zIndex: 100,
background: 'url("./imgs/rocker-bg.png") no-repeat center',
backgroundSize: 'contain'
});
document.body.appendChild(rockerBox);
let rockerBtn = document.createElement('div');
setStyle(rockerBtn,{
position: 'absolute',
width: `${innerDiameter}px`,
height: `${innerDiameter}px`,
left: `${centerNum}px`,
bottom: `${centerNum}px`,
borderRadius: `${innerRadius}px`,
background: '#fbbb1d',
});
rockerBox.appendChild(rockerBtn);
// 添加移动监控事件
let startPos = {x:0,y:0};
let disX = 0,disY = 0;
function onDown(e){
startPos.x = e.clientX || e.touches[0].clientX;
startPos.y = e.clientY || e.touches[0].clientY;
document.addEventListener('mousemove',onMove,false);
document.addEventListener('touchmove',onMove,false);
}
function onMove(e){
let clientX = e.clientX || e.touches[0].clientX;
let clientY = e.clientY || e.touches[0].clientY;
let maxNum = centerNum + 5;
disX = (clientX - startPos.x) ;
disY = (clientY - startPos.y) ;
// 圆心位置 (100,100) (div.style.x + 40, div.style.y + 40)
disX = disX > maxNum ? maxNum : (disX < -maxNum ? -maxNum : disX);
disY = disY > maxNum ? maxNum : (disY < -maxNum ? -maxNum : disY);
if ((Math.pow(disX,2) + Math.pow(disY,2)) > Math.pow(maxNum,2)) {
if (disY > 0) {
disY = Math.sqrt(Math.pow(maxNum, 2) - Math.pow(disX,2));
} else if (disY < 0) {
disY = -Math.sqrt(Math.pow(maxNum, 2) - Math.pow(disX,2));
}
}
rockerBtn.style.transform = `translate(${disX}px,${disY}px)`;
}
rockerUp = function (e){
document.removeEventListener('mousemove',onMove,false);
document.removeEventListener('touchmove',onMove,false);
disX = 0;
disY = 0;
rockerBtn.style.transform = 'translate(0,0)';
}
rockerBtn.addEventListener('mousedown',onDown,false);
document.body.addEventListener('mouseup',rockerUp,false);
rockerBtn.addEventListener('touchstart',onDown,false);
document.body.addEventListener('touchend',rockerUp,false);
function moveFrame(){
requestAnimationFrame(moveFrame);
if (Math.abs(disX) > Math.abs(disY)) {
if (disX > 0) {
// up [0,1] down [0,-1] left [-1,0] right [1,0]
dirImpulse = [1,0];// 设置行走方向
} else if (disX < 0) {
dirImpulse = [-1,0];
}
} else {
if (disY > 0) {
dirImpulse = [0,-1];
} else if (disY < 0) {
dirImpulse = [0,1] ;
}
}
}
moveFrame();
};
// 工具函数
function setStyle(dom,options,fn){
new Promise(function(resolve,reject){
for (let key in options){
dom.style[key] = options[key];
}
resolve();
}).then(res => {
if (fn) {
fn()
}
}).catch(err => {
console.log(err)
})
}
<view class="rocker-box" bindtouchstart="startFn" bindtouchmove="moveFn" bindtouchend="endFn">
<view class="rocker-inner" style="{{transform}}" ></view>
<image src='https://houtaicdn.alva.com.cn/medias/resources/wechat/arread/maze5/imgs/rocker-bg3.png' mode="widthFix"></image>
</view>
.rocker-box {
position: fixed;
right: 20rpx;
bottom: 20rpx;
z-index: 100;
width: 100rpx;
height: 100rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
/* background: url("https://houtaicdn.alva.com.cn/medias/resources/wechat/arread/maze5/imgs/rocker-bg.png") no-repeat center;
background-size: 'contain'; */
}
.rocker-box image {
position: absolute;
left: 0;
top: 0;
width: 100rpx;
height: 100rpx;
}
.rocker-inner {
width: 40rpx;
height: 40rpx;
/* background-color: #fbbb1d; */
background: url("https://houtaicdn.alva.com.cn/medias/resources/wechat/arread/maze5/imgs/rocker-i.png") no-repeat center;
background-size: 100% 100%;
border-radius: 50%;
z-index: 100;
}
const RADIUS = 27
const MIN_DISTANCE = 1.5 //至少位移到5才触发摇杆
// rocker相关
startPoint: "",
startFn(e) {
let touch = e.touches[0];
this.startPoint = [touch.pageX, touch.pageY];
},
moveFn(e) {
let touch = e.touches[0];
let point = [touch.pageX, touch.pageY];
let differ = [point[0] - this.startPoint[0], point[1] - this.startPoint[1]];
let distance = Math.sqrt(differ[0] * differ[0] + differ[1] * differ[1]);
if (distance > MIN_DISTANCE) {
// 摇杆始终切到边缘,实际位移距离无关紧要,只关心移动方向
let rate = RADIUS / distance;
let position = [differ[0] * rate, differ[1] * rate];
this.setData({
transform: "transform: translate(" + position[0] + "px, " + position[1] + "px)"
});
if (Math.abs(position[0]) > Math.abs(position[1])) {
if (position[0] < 0) {
threeBusiness.setDirImpulse(-1, 0);// 设置行走方向
} else if (position[0] > 0) {
threeBusiness.setDirImpulse(1, 0);
}
} else {
if (position[1] < 0) {
threeBusiness.setDirImpulse(0, 1);
} else if (position[1] > 0) {
threeBusiness.setDirImpulse(0, -1);
}
}
}
},
endFn(e) {
this.setData({
transform: ""
});
},
three.js没有自己的物理引擎,需要引入外部物理引擎来实现物理效果
渲染引擎和物理引擎结合来实现碰撞检测、自由落体等物理现象,其核心实质就是将物理引擎的计算结果更新到渲染引擎中。
(待更新…)
import { DeviceOrientationControls } from './jsm/controls/DeviceOrientationControls.js';
deviceControl = new DeviceOrientationControls(camera);
function animate() {
window.requestAnimationFrame( animate );
deviceControl.update();
renderer.render( scene, camera );
}
const deviceOrientationControl = require("./DeviceOrientationControl.js");
var deviceMotion = false; // 是否使用陀螺仪保持原位
var lastDevice = {},
device = {};
function moveAnimate() {
animateId = canvas.requestAnimationFrame(moveAnimate);
if (
lastDevice.alpha !== device.alpha ||
lastDevice.beta !== device.beta ||
lastDevice.gamma !== device.gamma
) {
// 手机的方位发生了移动
lastDevice.alpha = device.alpha;
lastDevice.beta = device.beta;
lastDevice.gamma = device.gamma;
if (deviceMotion) {
// 操作模型使保持在空间中的原位
deviceOrientationControl.deviceControl(
camera,
device,
THREE,
isAndroid
);
}
}
renderer.render(scene, camera);
}
function startDeviceMotion() {
deviceMotion = true;
wx.onDeviceMotionChange(function (_device) {
device = _device;
});
wx.startDeviceMotionListening({
interval: "ui",
success: function () {
console.log("startDeviceMotionListening", "success");
},
fail: function (error) {
console.log("startDeviceMotionListening", error);
},
});
}
function stopDeviceMotion() {
deviceMotion = false;
wx.offDeviceMotionChange();
wx.stopDeviceMotionListening({
success: function () {
console.log("stopDeviceMotionListening", "success");
},
fail: function (error) {
console.log("stopDeviceMotionListening", error);
},
});
}
const { screenshot } = require("./screenshot");
takePhoto() {
let self = this;
wx.showLoading({
title: "图片处理中...",
});
this.data.cameraContext.takePhoto({
quality: "low",
success: res => {
self.webglToPhoto(res.tempImagePath);
},
});
},
webglToPhoto(photo) {
let self = this;
const query = wx.createSelectorQuery();
query
.select("#canvas")
.fields({ node: true, size: true })
.exec(res => {
self.clipWebgl(res[0].node, photo).then(webglImg => {
const ctx = wx.createCanvasContext("photo");
const { width, height, platform } = self.data.sysInfo;
ctx.drawImage(photo, 0, 0, width, height);
ctx.drawImage(webglImg.path, 0, 0, width, height);
ctx.draw(true);
self.canvasToImg(ctx, width, height);// canvas已经绘制成功了,但小程序中canvas元素不能缩放展示,要想缩放展示尺寸,就要转成img再展示img
});
});
return;
},
clipWebgl(helperCanvas,photo){
return new Promise((resolve,reject) => {
const [data, w, h] = screenshot(renderer, scene, camera, THREE.WebGLRenderTarget);
// resolve(data.buffer);
const ctx = helperCanvas.getContext('2d');
const imgData = helperCanvas.createImageData(data, w, h);
helperCanvas.height = imgData.height;
helperCanvas.width = imgData.width;
ctx.putImageData(imgData, 0, 0);
wx.canvasToTempFilePath({
canvas: helperCanvas,
success(res) {
resolve({path: res.tempFilePath, width: imgData.width, height: imgData.height})
},
fail(err){
reject(err);
}
})
})
},
canvasToImg(ctx, width, height) {
let self = this;
ctx.draw(
true,
setTimeout(() => {
wx.canvasToTempFilePath({
quality: 0.5,
x: 0,
y: 0,
width: width,
height: height,
destWidth: width * wx.getSystemInfoSync().pixelRatio,
destHeight: height * wx.getSystemInfoSync().pixelRatio,
canvasId: "photo",
fileType: "jpg",
success(res) {
console.log("photo绘制成功");
wx.hideLoading();
self.setData({
photoUrl: res.tempFilePath
});
}
});
}, 300)
);
},
function flip(pixels, w, h, c) {
// handle Arrays
if (Array.isArray(pixels)) {
var result = flip(new Float64Array(pixels), w, h, c);
for (var i = 0; i < pixels.length; i++) {
pixels[i] = result[i];
}
return pixels;
}
if (!w || !h) throw Error('Bad dimensions');
if (!c) c = pixels.length / (w * h);
var h2 = h >> 1;
var row = w * c;
var Ctor = pixels.constructor;
// make a temp buffer to hold one row
var temp = new Ctor(w * c);
for (var y = 0; y < h2; ++y) {
var topOffset = y * row;
var bottomOffset = (h - y - 1) * row;
// make copy of a row on the top half
temp.set(pixels.subarray(topOffset, topOffset + row));
// copy a row from the bottom half to the top
pixels.copyWithin(topOffset, bottomOffset, bottomOffset + row);
// copy the copy of the top half row to the bottom half
pixels.set(temp, bottomOffset);
}
return pixels;
};
function screenshot(renderer, scene, camera, WebGLRenderTarget) {
// const { width, height } = renderer.domElement;
const width = 720,height = 1280;
const renderTarget = new WebGLRenderTarget(width, height);
const buffer = new Uint8Array(width * height * 4);
renderTarget.texture.encoding = renderer.outputEncoding;
renderer.setRenderTarget(renderTarget);
renderer.render(scene, camera);
renderer.readRenderTargetPixels(renderTarget, 0, 0, width, height, buffer);
renderer.setRenderTarget(null);
renderTarget.dispose();
flip(buffer, width, height, 4);
return [buffer, width, height];
}
export {screenshot}
import BigMoveScript from './script/BigMoveScript';
addRigidbody(){
let mat2 = new Laya.BlinnPhongMaterial();
Laya.Texture2D.load("res/plywood.jpg", Laya.Handler.create(this, function (tex) {
mat2.albedoTexture = tex;
}));
var raidius = 0.5;
var height = 1;
this.rigidMesh = this.scene.addChild(new Laya.MeshSprite3D(Laya.PrimitiveMesh.createCapsule(raidius, height)));// 创建角色碰撞器
this.rigidMesh.meshRenderer.material = this.mat2;
let cPos = camera.transform.position;
this.rigidMesh.transform.position = new Laya.Vector3(cPos.x,1,cPos.z);
this.rigidMesh.transform.rotation = new Laya.Vector3(0,-1.8,0);
this.rigidMesh.addComponent(BigMoveScript);
let rigidBody = this.rigidMesh.addComponent(Laya.CharacterController);
// rigidBody.upAxis = new Laya.Vector3(0,1,0);
// let rigidBody = this.rigidMesh.addComponent(Laya.Rigidbody3D);
let sphereShape = new Laya.CapsuleColliderShape(raidius, height);
rigidBody.colliderShape = sphereShape;
rigidBody.mass = 1;
rigidBody.gravity = new Laya.Vector3(0,0,0);
// this.rockerView = new JoyStick(this.rigidMesh);
}
animate(){
var _this = this;
if (this.rigidMesh) {
camera.transform.rotation = this.rigidMesh.transform.rotation;
camera.transform.position = this.rigidMesh.transform.position;
}
requestAnimationFrame(_this.animate.bind(_this));
}
export default class BigMoveScript extends Laya.Script3D{// 操作方法:点击开始行走,再点击暂停,在屏幕上移动手指旋转;
constructor(){
super();
this.scene = null;
this.lastPosition = new Laya.Vector2(0, 0);
this.distance = 0.0;
this.disFirstTouch = new Laya.Vector2(0, 0);
this.disLastTouch = new Laya.Vector2(0, 0);
this.isTwoTouch = false;
this.first = true;
this.twoFirst = true;
this.rotate = new Laya.Vector3(0,0,0);
this.modelRotate = false;//模型默认自转,当用户点击到屏幕时取消自转
this.modelMove = false;// 模型自动漫游
}
onStart(){
this.scene = this.owner.parent;
// this.rigidBody = this.owner._components[1]
this.rigidBody = this.owner.getComponent(Laya.CharacterController)
this.firstRotate = new Laya.Quaternion(this.owner.transform.rotation.x,this.owner.transform.rotation.y,this.owner.transform.rotation.z,this.owner.transform.rotation.w);
this.firstPosition = new Laya.Vector3(this.owner.transform.position.x,this.owner.transform.position.y,this.owner.transform.position.z);
Laya.stage.on(Laya.Event.CLICK,this,this.toggleMovestate);
}
toggleMovestate(){
this.modelMove = !this.modelMove;
}
onUpdate(){
var mod = this.owner;
if (this.modelRotate) {
mod.transform.rotate(new Laya.Vector3(0,0.05,0),false,false);
}
if (this.modelMove) {
let modR = mod.transform.rotationEuler.y / 180 * Math.PI;
let x = -Math.sin(modR);
let z = -Math.cos(modR);
// this.rigidBody.linearVelocity = new Laya.Vector3(x,0,z);
this.rigidBody.move(new Laya.Vector3(x/40,0,z/40));
} else {
// this.rigidBody.linearVelocity = new Laya.Vector3(0,0,0);
this.rigidBody.move(new Laya.Vector3(0,0,0));
}
let touchCount = this.scene.input.touchCount();// 获取触摸点个数
if (1 === touchCount){
//判断是否为两指触控,撤去一根手指后引发的touchCount===1
if(this.isTwoTouch){
return;
}
//获取当前的触控点,数量为1
let touch = this.scene.input.getTouch(0);// 获取触摸点,参数代表索引
//是否为新一次触碰,并未发生移动
if (this.first){
//获取触碰点的位置
this.lastPosition.x = touch._position.x;
this.lastPosition.y = touch._position.y;
this.first = false;
}
else{
//移动触碰点
let deltaY
deltaY = this.lastPosition.y - touch._position.y;
let deltaX = touch._position.x - this.lastPosition.x;
this.lastPosition.x = touch._position.x;
this.lastPosition.y = touch._position.y;
//根据移动的距离进行旋转
this.rotate.setValue(0, deltaX / 50, 0);
mod.transform.rotate(this.rotate,false,false);
}
}
else if (2 === touchCount){
this.modelMove = false;
this.isTwoTouch = true;
//获取两个触碰点
let touch = this.scene.input.getTouch(0);
let touch2 = this.scene.input.getTouch(1);
//是否为新一次触碰,并未发生移动
if (this.twoFirst){
//获取触碰点的位置
this.disFirstTouch.x = touch.position.x - touch2.position.x;
this.disFirstTouch.y = touch.position.y - touch2.position.y;
this.distance = Laya.Vector2.scalarLength(this.disFirstTouch);// 计算标量长度
// console.log('First Distance',this.distance);
this.touchAFirstX = touch.position.x;
this.touchAFirstY = touch.position.y;
this.touchBFirstX = touch2.position.x;
this.touchBFirstY = touch2.position.y;
this.twoFirst = false;
}
else{
// 移动
this.touchALastX = touch.position.x;
this.touchALastY = touch.position.y;
this.touchBLastX = touch2.position.x;
this.touchBLastY = touch2.position.y;
let centerFirst = new Laya.Point((this.touchAFirstX + this.touchBFirstX) / 2, (this.touchAFirstY + this.touchBFirstY) / 2);
let centerLast = new Laya.Point((this.touchALastX + this.touchBLastX) / 2, (this.touchALastY + this.touchBLastY) / 2);
let moveX,moveY;
moveX = (centerFirst.x - centerLast.x) / 100;
moveY = (centerFirst.y - centerLast.y) / 100;
let modR = mod.transform.rotationEuler.y / 180 * Math.PI,x,z;
if (Math.abs(moveY) > Math.abs(moveX)) {
if (moveY > 0) {// go
} else if (moveY < 0) {// back
modR += 180;
}
} else {
if (moveX > 0) {// left
modR += 90;
} else if (moveX < 0) {// right
modR -= 90;
}
}
x = -Math.sin(modR);
z = -Math.cos(modR);
this.rigidBody.move(new Laya.Vector3(x/40,0,z/40));
this.touchAFirstX = touch.position.x;
this.touchAFirstY = touch.position.y;
this.touchBFirstX = touch2.position.x;
this.touchBFirstY = touch2.position.y;
}
}
else if (0 === touchCount){
this.first = true;
this.twoFirst = true;
this.lastPosition.x = 0;
this.lastPosition.y = 0;
this.isTwoTouch = false;
}
}
}
export default class AutoGo extends Laya.Script3D{
constructor(){
super();
this.scene = null;
this.speed = 1;
}
/**
* 第一次执行update之前执行,只会执行一次
*/
onStart(){
this.scene = this.owner.parent;
this.pathConfig = [// 路线配置坐标
[21,1.5,-0.8],
[25,1.5,-0.8],
[25,1.5,11.5],
[9,1.5,11.5],
[9,1.5,-0.8],
];
this.nextIndex = 1;
this.firstRotate = new Laya.Vector3(); this.owner.transform.rotationEuler.cloneTo(this.firstRotate);
this.firstPos = new Laya.Vector3(); this.owner.transform.position.cloneTo(this.firstPos);
// this.rigidBody = this.owner._components[1]
this.rigidBody = this.owner.getComponent(Laya.CharacterController)
}
setSpeed(s){
this.speed = s;
}
/**
* 脚本每次启动后执行,例如被添加到一个激活的对象上或者设置脚本的enabled = true
*/
_onEnable(){
this._onUpdate();
}
_onDisable(){
cancelAnimationFrame(this.animateId);
this.reset();
}
reset(){
if(this.firstRotate && this.firstPos) {
this.nextIndex = 1;
this.owner.transform.rotation = new Laya.Vector3(this.firstRotate.x,this.firstRotate.y,this.firstRotate.z);
this.owner.transform.position = new Laya.Vector3(this.firstPos.x,this.firstPos.y,this.firstPos.z);
console.log('重置:',this.owner.transform.rotation,this.owner.transform.position)
}
}
/**
* 每帧更新时执行
*/
_onUpdate(){
var mod = this.owner;
if (!this.rigidBody || !this.pathConfig){
this.animateId = requestAnimationFrame(this._onUpdate.bind(this));
return;
}
if (!mod.targetPosition) {
mod.targetPosition = new Laya.Vector3(mod.transform.position.x,mod.transform.position.y,mod.transform.position.z);
console.log('起始坐标位置',mod.targetPosition);
mod.alpha = 0;
}
var dis = Laya.Vector3.distance(mod.transform.position, mod.targetPosition);
if (dis < 0.3) {
this.rigidBody.move(new Laya.Vector3(0,0,0));
let newTarget = new Laya.Vector3(this.pathConfig[this.nextIndex][0], this.pathConfig[this.nextIndex][1], this.pathConfig[this.nextIndex][2]);
// 计算转向夹角
let modR = mod.transform.rotationEuler.y / 180 * Math.PI;
let x = -Math.sin(modR);
let z = -Math.cos(modR);
let aV = new Laya.Vector3(x,0,z);
let bV = new Laya.Vector3(0,0,0);
Laya.Vector3.subtract(newTarget, mod.transform.position,bV);
let cosAlpha = Laya.Vector3.dot(aV,bV) / (Laya.Vector3.scalarLength(aV) * Laya.Vector3.scalarLength(bV));
let alpha = Math.acos(cosAlpha);
// console.log(alpha)// 此值始终大于0小于Math.PI;
if (alpha < -0.1 || alpha > 0.1) {
let cross = (aV.x * bV.z - bV.x * aV.z);
let g = cross > 0 ? -0.03 : 0.03;// aV在bV的逆时针方向为-0.03;aV在bV的顺时针方向为0.03;
mod.transform.rotate(new Laya.Vector3(0,g,0));
this.animateId = requestAnimationFrame(this._onUpdate.bind(this));
return;
}
// mod.transform.rotate(new Laya.Vector3(0,alpha,0),false,true);
// 计算下一段路的单位步伐;
var dir = new Laya.Vector3();
Laya.Vector3.subtract(newTarget, mod.transform.position, dir);
Laya.Vector3.normalize(dir, dir);
Laya.Vector3.scale(dir, 0.04 * this.speed, dir);
mod.speed = dir;
mod.targetPosition = newTarget;
console.log('当前位置',mod.transform.position);
console.log('下一目标位置',newTarget);
if (this.nextIndex === this.pathConfig.length - 1) {
this.nextIndex = 0;
} else {
this.nextIndex++;
}
} else {
// console.log('move');
this.rigidBody.move(mod.speed);
}
this.animateId = requestAnimationFrame(this._onUpdate.bind(this));
}
}
var texture = textureLoader.load(tex_url);
texture.minFilter = THREE.LinearFilter;// 解决问题的关键代码
var material = new THREE.MeshBasicMaterial(
{
map: texture
}
);
currModel.layers.set(1);
currModel.traverse(item => {
item.layers.set(1);
});
搜索REVISION
即可
three.js详细学习文档
three.js历史版本代码
three editer
three.js微信小程序127版本demo
three.js微信小程序127demo简化版
three.js微信小程序视频纹理demo