threejs 初识
用于展示3D动效,就是 跟拍电影一样,需要有3大模块:scene,camera,renderer。
- scene:场景,用于放置用到的模型。
- camera:摄像机,拍电影似的,得有个摄像机。
- renderer:渲染器,很重要,用于与被渲染的dom元素挂钩,也用于 与场景、摄像机 挂钩。
一个动效的制作过程如下:
-
得到三大基础模块:scene, camera, renderer
-
绘制结果与DOM挂钩
-
加载摄像头控制器(可选),用于 旋转、缩放等效果
-
添加场景内 模型
-
渲染
-
事件设置(可选)
示例是一个 星球,顶点上 是 文字。可旋转,缩放:
<html> <head> <title>球状旋转_sprite_v2.html</title> <meta charset="utf-8"> <style> body { margin: 0; } </style> </head> <body> </body> <script src="js/three.js"></script> <script src="js/OrbitControls.js"></script> <script> /** * 3d加载步骤 * 1、得到三大基础模块:scene, camera, renderer * 2、绘制结果与DOM挂钩 * 3、加载摄像头控制器(可选) * 4、添加场景内 模型 * 5、渲染 * 6、事件设置(可选) */ var scene, camera, renderer, controls, group; var container; var startX, startY; container = document.createElement( 'div' ); document.body.appendChild( container ); /*1*/ scene = new THREE.Scene(); camera = new THREE.PerspectiveCamera( 40, window.innerWidth/window.innerHeight, 1, 1000 ); camera.position.z = 50; renderer = new THREE.WebGLRenderer(); renderer.setSize( window.innerWidth, window.innerHeight ); /*2*/ container.appendChild( renderer.domElement ); /*3*/ // controls controls = new THREE.OrbitControls( camera, renderer.domElement ); controls.minDistance = 20; controls.maxDistance = 50; controls.maxPolarAngle = Math.PI / 2; /*4*/ group = new THREE.Group(); scene.add( group ); var vertices = new THREE.DodecahedronGeometry( 10 ).vertices; //多个精灵 vertices.forEach(function (item, index) { var sprite = createSpriteTextNoPosition(index); sprite.position.set(item.x, item.y, item.z); sprite.clickFlag = true; sprite.aa = '序号:' + index; group.add(sprite); }); /*5*/ var animate = function () { requestAnimationFrame( animate ); // group.rotation.x += 0.005; group.rotation.y += 0.005; renderer.render( scene, camera ); }; animate(); /*6*/ //监听移动端touchstart事件 function onTouchStart(e) { // console.log('触摸开始') // console.log(e) var touch = e.touches[0]; //获取第一个触点 var x = Number(touch.pageX); //页面触点X坐标 var y = Number(touch.pageY); //页面触点Y坐标 //记录触点初始位置 startX = x; startY = y; } //监听移动端touchEnd事件 function onTouchEnd(e) { // console.log('触摸结束') // console.log(e); if (e.changedTouches[0].pageX == startX && e.changedTouches[0].pageY == startY) getClickMap(e); } function getClickMap(event) { event.preventDefault(); var mouse = {}; mouse.x = (event.clientX / window.innerWidth) * 2 - 1; mouse.y = -(event.clientY / window.innerHeight) * 2 + 1; if(event.touches) { mouse.x = (event.changedTouches[0].pageX / window.innerWidth) * 2 - 1; mouse.y = -(event.changedTouches[0].pageY / window.innerHeight) * 2 + 1; } var vector = new THREE.Vector3(mouse.x, mouse.y, 0.5); vector = vector.unproject(camera); var raycaster = new THREE.Raycaster(camera.position, vector.sub(camera.position).normalize()); var intersects = raycaster.intersectObjects(group.children); //TODO 这个50 距离原点的距离, 正好就是 摄像头 到 球心的距离应该是。 if (intersects.length > 0 && intersects[0].distance < 50 && intersects[0].object.clickFlag) { console.log(intersects[0].object.aa); } } function createSpriteTextNoPosition(index){ //先用画布将文字画出 let canvas = document.createElement("canvas"); canvas.width = 400; let ctx = canvas.getContext("2d"); ctx.fillStyle = "#ffff00"; ctx.font = "Bold 100px Arial"; ctx.lineWidth = 4; ctx.fillText("萨克拉" + index,4,104); let texture = new THREE.Texture(canvas); texture.needsUpdate = true;//不设置,会 不出现 //使用Sprite显示文字 let material = new THREE.SpriteMaterial({map:texture}); let textObj = new THREE.Sprite(material); textObj.scale.set(4, 2, 1); return textObj; } document.addEventListener('mousedown', getClickMap, false); document.addEventListener('touchstart', onTouchStart, false); document.addEventListener('touchend', onTouchEnd, false); </script> </html>
实现过程中遇到了一些问题,这里特做以记录:
1、geometry 带有顶点的 几何图形
2、获取几何图形的顶点,数组
利用属性 vertices
3、group 组的概念,用于 多个模型分组。
常用于 整个组 内容 一起 旋转之类。
4、group.children 可获取到 组内所有的 模型。scene.children同理
5、光投影
6、orbitControls 与移动端 touchmove事件冲突,不能共存
7、利用 touchStart 和 touchEnd 判断是否为点击事件
利用 touch事件对象 的 changedTouches[0];start和 end皆有; 如果 start里的 坐标 等于 end 里的 坐标,则为点击。
8、模型上移
不做其他改变,使用定位,将画布绝对定位 top=-100px;点击事件里 pageY + 100
9、bug:手机浏览器 画布过大,超过屏幕,与google调试不一致;
强行指定canvas给渲染器,并 important 宽高 为100%。
10、球形顶点 多点旋转:点与数据量实施方案
顶点数须大于数据量:遍历顶点,数据量不够则再来一遍。铺满
11、MeshPhongMaterial 镜面材质,需要灯光
12、MeshBasicMaterial 基础材质,普通上色,不需灯光
13、动效切换,清除
a、停止动效 (必须cancelAnimationFrame)
b、清除场景内模型 (需要深清除,内存里的)
移除场景内的子元素,需注意 不要移除 摄像机和灯光(子元素包含了)。
c、有绑定事件的 移除事件
14、canvas文本显示不清晰,
解决:使用textObj.scale.set(4,2,1); 对sprite 进行缩放,参数是 缩放量,三维的。改变前两参数就行