Three.js
Three.js是一款运行在浏览器中的 3D 引擎(基于WebGL的API的封装),你可以用它来创造你所需要的一系列3D动画场景。
基本思路
- 创建基本的元素
- 创建透明二维矩形并贴图模拟手机
- 创建透明二维矩形并贴图显示前景
- 遍历随机创建其他装饰小物体
- 通过监听鼠标移动判断移入事件
- 通过移入事件和位置设置手机偏移
- tween.js实现物体偏移过渡
- 渲染时加入装饰小物体动画
- 定时改变装饰小物体移动方向
获取源码
https://github.com/jiangzetian/CanvasDemo/tree/master/filp-card
项目结构
filp-card//项目根目录
|
├── index.html (项目入口)
|
├── css
│ └── index.css//样式文件
├── img
│ ├── pic1.png//图片
| └── pic2.png//图片
└── js
└── threejs
├── OrbitControls.js//控制库
├── three.js//three库
├── three.min.js//three库压缩
├── tweenjs.js//动画库
└── tweenjs.min.js//动画库压缩
基本代码
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>翻转卡片</title>
<link href="css/index.css" rel="stylesheet" />
<script src="js/threejs/three.js"></script>
<script src="js/threejs/tweenjs.js"></script>
<script src="js/threejs/OrbitControls.js"></script>
</head>
<body>
<div id="retina"></div>
</body>
<script>
//页面加载完成
window.onload = function() {
create();
}
function create() {
initRender(); //创建渲染器
initCamera(); //创建相机
initLight(); //创建光源
initObject(); //创建物体
initBgCubes();//创建装饰物体
initSprite(); //创建图片
initScene(); //创建场景
//initControls()//视角控制器 开发时观察用
initMouse() //鼠标监听
initBgCubesAnime();//物体移动控制
render(); //渲染
}
//渲染器
var renderer;
var width;
var height;
function initRender() {}
//相机
var camera;
var origPoint = new THREE.Vector3(0, 0, 0);
function initCamera() {}
//光源
var pointLight;
var ambientLight;
function initLight() {}
//物体1
var cube;
function initObject() {}
//装饰物体
var bgCubes = [];
var count = 20;
function initBgCubes() {}
//图片1
var image_1;
function initSprite() {}
//创建场景
var scene;
function initScene() {}
//交互插件
var controls;
function initControls() {}
//装饰物体移动
var cubeV = {v:-0.1}
function initBgCubesAnime(){}
//渲染
function render() {}
//鼠标监听
function initMouse() {}
</script>
</html>
完整代码
<!--路径:/index.html-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>翻转卡片</title>
<link href="css/index.css" rel="stylesheet" />
<script src="js/threejs/three.js"></script>
<script src="js/threejs/tweenjs.js"></script>
<script src="js/threejs/OrbitControls.js"></script>
</head>
<body>
<div id="retina"></div>
</body>
<script>
//页面加载完成
window.onload = function() {
create();
}
function create() {
initRender(); //创建渲染器
initCamera(); //创建相机
initLight(); //创建光源
initObject(); //创建物体
initBgCubes();//创建装饰物体
initSprite(); //创建图片
initScene(); //创建场景
// initControls()//视角控制器
initMouse() //鼠标监听
initBgCubesAnime();//物体移动控制
render(); //渲染
}
//渲染器
var renderer;
var width;
var height;
function initRender() {
width = window.innerWidth;
height = window.innerHeight;
renderer = new THREE.WebGLRenderer({
antialias: true, //抗锯齿开启
})
renderer.setSize(width, height); //设置渲染器宽高
renderer.setClearColor('#000000', 1.0); //设置背景颜色
renderer.setPixelRatio(window.devicePixelRatio); //设置设备像素比
document.getElementById('retina').appendChild(renderer.domElement); //把渲染器放入页面中
}
//相机
var camera;
var origPoint = new THREE.Vector3(0, 0, 0);
function initCamera() {
camera = new THREE.PerspectiveCamera(45, width / height, 1, 2000);
camera.position.set(0, 0, 900); //相机位置
camera.up.set(0, 1, 0) //设置相机正方向
camera.lookAt(origPoint); //设置相机视点
}
//光源
var pointLight;
var directionalLight ;
var ambientLight;
function initLight() {
//点光源
pointLight = new THREE.PointLight(0xffffff);
pointLight.position.set(-800, 0, 1000);
directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight .position.set(-1000, 0, 0);
//环境光
ambientLight = new THREE.AmbientLight(0xffffff);
ambientLight.position.set(0, 0, 2000);
}
//物体1
var cube;
function initObject() {
var spriteMap = new THREE.TextureLoader().load("img/pic1.png");
var geometry = new THREE.PlaneBufferGeometry(260,440);
var material = new THREE.MeshMatcapMaterial({
map: spriteMap,
transparent: true,
});
cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 0, 0);
cube.name = 'cube';
}
var bgCubes = [];
var count = 20;
function initBgCubes() {
for(let i=0;i<count;i++){
let geometry;
let rand = Math.round(Math.random()*(8-1)+1);
switch(rand){
case 1:
geometry = new THREE.BoxGeometry(30, 30, 30);
break;
case 2:
geometry = new THREE.TorusBufferGeometry(20, 8, 16, 100);
break;
case 3:
geometry = new THREE.ConeGeometry(15, 40, 32);
break;
case 4:
geometry = new THREE.CylinderGeometry(10, 10, 30, 32);
break;
case 5:
geometry = new THREE.TetrahedronBufferGeometry(20);
break;
case 6:
geometry = new THREE.SphereGeometry( 15, 32, 32 );
break;
case 7:
geometry = new THREE.OctahedronGeometry( 15 );
break;
case 8:
geometry = new THREE.DodecahedronGeometry( 15 );
break;
}
let material = new THREE.MeshPhongMaterial({
color: 0x3546FF,
});
let cube = new THREE.Mesh(geometry, material);
if(i<count/2){
let cw = width;
let ch = height-150;
let r = 500;
let maxcount = count/2;
let x = cw/2-(cw/(maxcount+1))*(i+1);
let y = ch/2-(ch/(maxcount+1))*(i+1);
let z = Math.pow(r,2)-Math.pow(cw/2-(cw/(maxcount+1))*(i+1),2);
cube.position.set(x, y-50, Math.sqrt(z));
}else{
let cw = width;
let ch = height-150;
let r = 500;
let maxcount = count/2;
let index = i - count/2;
let x = cw/2-(cw/(maxcount+1))*(index+1);
let y = ch/2-(ch/(maxcount+1))*(index+1);
let z = Math.pow(r,2)-Math.pow(cw/2-(cw/(maxcount+1))*(index+1),2);
cube.position.set(x, y, -Math.sqrt(z));
}
bgCubes.push(cube)
}
}
//图片1
var image_1;
function initSprite() {
var spriteMap = new THREE.TextureLoader().load("img/pic2.png");
var geometry = new THREE.PlaneBufferGeometry(220, 400);
var material = new THREE.MeshBasicMaterial({
map: spriteMap,
transparent: true,
});
image_1 = new THREE.Mesh(geometry, material);
image_1.position.set(0, 0, 100);
}
//创建场景
var scene;
function initScene() {
scene = new THREE.Scene();
scene.add(pointLight);
scene.add(directionalLight);
scene.add(ambientLight);
scene.add(cube);
for(let i=0;i<bgCubes.length;i++){
scene.add(bgCubes[i]);
}
scene.add(image_1);
}
//交互插件
var controls;
function initControls() {
controls = new THREE.OrbitControls(camera, renderer.domElement);
}
//装饰物体移动
var cubeV = {v:-0.1}
function initBgCubesAnime(){
setInterval(()=>{
if(cubeV.v>0){
createjs.Tween.get(cubeV).to({v:-0.1}, 1000).addEventListener('change', e => {
console.log(cubeV)
}, false);
}else{
createjs.Tween.get(cubeV).to({v:0.1}, 1000).addEventListener('change', e => {
console.log(cubeV)
}, false);
}
},6000)
}
//渲染
function render() {
renderer.clear();
for(let i=0;i<bgCubes.length;i++){
bgCubes[i].rotation.x += Math.round(Math.random()*(5-1)+1)/1000;
bgCubes[i].rotation.y += Math.round(Math.random()*(5-1)+1)/1000;
bgCubes[i].rotation.z += Math.round(Math.random()*(5-1)+1)/1000;
bgCubes[i].position.x += cubeV.v*Math.round(Math.random()*(2-1)+1);
bgCubes[i].position.y += cubeV.v*Math.round(Math.random()*(2-1)+1);
bgCubes[i].position.z += cubeV.v*Math.round(Math.random()*(2-1)+1);
}
renderer.render(scene, camera);
requestAnimationFrame(render);
}
//鼠标监听
function initMouse() {
var mouse = new THREE.Vector2();
var raycaster = new THREE.Raycaster();
//移动角度数据
var rotationData = {
x: 0,
y: 0
};
//移动距离数据(图片)
var positionData = {
x: 0,
y: 0
};
//鼠标移动监听
document.onmousemove = function(e) {
var mx = e.clientX;
var my = e.clientY;
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
for (let i = 0; i < intersects.length; i++) {
if (intersects[i].object.name == 'cube') {
createjs.Tween.get(rotationData).to({
x: Math.PI / 12 * (my - height / 2) / 250,
y: Math.PI / 12 * (mx - width / 2) / 150
}, 300).addEventListener('change', e => {
cube.rotation.set(rotationData.x, rotationData.y, 0);
image_1.rotation.set(rotationData.x, rotationData.y, 0);
}, false);
createjs.Tween.get(positionData).to({
x: 25 * (mx - width / 2) / 150,
y: -25 * (my - height / 2) / 250
}, 300).addEventListener('change', e => {
image_1.position.set(positionData.x, positionData.y, 100);
}, false);
}
}
} else {
createjs.Tween.get(rotationData).to({
x: 0,
y: 0
}, 300).addEventListener('change', e => {
cube.rotation.set(rotationData.x, rotationData.y, 0);
image_1.rotation.set(rotationData.x, rotationData.y, 0);
}, false);
createjs.Tween.get(positionData).to({
x: 0,
y: 0
}, 300).addEventListener('change', e => {
image_1.position.set(positionData.x, positionData.y, 100);
}, false);
}
};
}
</script>
</html>