算法
有效数独 判断一个 9x9 的数独是否有效。
var isValidSudoku = function(board) {
var pieceMap = {};
columnsMap = {}
var res = board.some((arr, index1) => {
var numMap = {};
return arr.some((item, index2) => {
pieceKey = Math.floor(index1/3) + '' + Math.floor(index2/3);
if(!pieceMap[pieceKey]){
pieceMap[pieceKey] = {}
}
if(item === '.'){
return false
}
if(numMap[item]){
return true;
}else{
numMap[item] = true;
}
if(pieceMap[pieceKey][item]){
return true;
}else{
pieceMap[pieceKey][item] = true;
}
if(!columnsMap[index2]){
columnsMap[index2] = {};
}
if(columnsMap[index2][item]){
return true
}else{
columnsMap[index2][item] = true;
}
})
});
if(res){
return false
}
return true
};
n*n 数组矩阵旋转
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
]
// 转换成
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
const rotate = (matrix) => {
matrix.reverse()
for (let i = 0; i < matrix.length; i++) {
for (let j = i + 1; j < matrix[0].length; j++) {
let tmp = matrix[i][j]
matrix[i][j] = matrix[j][i]
matrix[j][i] = tmp
}
}
};
3D 动画
canvas.getContext(contextType, contextAttributes);
// canvas 获取绘图上下文
var context = canvas.getContext('2d');
// WebGL 获取绘图上下文
var gl = canvas.getContext('webgl') // 或 experimental-webgl
Canvas 是浏览器封装好的一个绘图环境,在实际进行绘图操作时,浏览器仍然需要调用 OpenGL API。而 WebGL API 几乎就是 OpenGL API 未经封装,直接套了一层壳,threejs 是一个 webGL 框架。
webGL 整体就是用来在 canvas 上绘制 3D 效果的 API。目前封装的比较好的是 threejs。
首先一个 3D 动画由这么几部分元素组成。容器、舞台、演员、天空、相机、控制器等
容器
动画需要有个承载的体现,这个我们一般称为容器,在这里我们一般认为 canvas 标签就是容器。这个容器是用来展示动画的载体。整个容器可以认为是一个无边无际的三维坐标系,并且有正有负。
舞台
舞台就是一个动画的场景,如果我们认为动画是一个 3D 的场景,一个小的动画只用一个场景就可以,但是一个复杂的大型的动画是由一个一个场景组合起来的。每个场景又有自己的动画,区分场景是为了将大的动画分成几组来完成,降低复杂度。
一个舞台就是在这个无边无际的坐标系中的其中一块立体空间。
演员
有了舞台就需要演员来完成动画,动画场景中的每一个动画元素都可以称为一个演员,演员也可以分组,一个复杂的演员可以由多个小的动画元素组成,而每一个动画元素也可以称为一个演员,主要是看其相关性,将演员分组也是降低复杂度的一种。
演员基本上就是场景中的动画元素了,一个复杂的场景动画演员有自己的运动轨迹,演员在场景中完成自己的动画轨迹就完成了自己的动画,如果演员的运动轨迹是由其他因素所决定的,那就根据自己的轨迹去完成。
天空
我们可以理解为整个 3D 动画的场景是被放在一个立体的盒子中的,天空就是盒子内部的内表皮,盒子内部的六个面都可以有自己的颜色图片,我们在盒子内部的舞台中,看起来这个就像是动画的天空。
当然外表皮也是可以着色的,但我们一般是把相机放在舞台内部,所有外表皮的意义并不大。
相机
这个是非常重要的,与其说相机不如说是镜头摆放的位置,threejs 中有两种摄像机方式,正交投影和透视投影两种,最好自己有过实际的 demo 来体会一下两种相机的不同。
相机摆放在不同的位置就能看到不同角度位置的动画,在立体坐标系中,将相机让在某一个舞台面前就能看到这个舞台上的动画。主要是看动画想以哪种形式进行展现,然后设置好相机的位置。动画场景的切换也是通过移动相机,将摄像机放到下一个场景的位置。
控制器
一般情况下我们动画只是播放,很少有交互,简单的交互点击,拖动这些是不用设计到控制器的。控制器是控制动画展现的位置的东西,其实就是控制摄像头的摆放,根据不同的交互类型让摄像头按照某种规律进行运动,主要用于游戏中的交互,可以自行写个 demo 试试每种控制器都有什么方式,分为下面几类:
- OrbitControls:旋转控制器
- FlyControls:飞行控制器
- PointerLockControls:指针控制器
- TrackballControls:轨迹球控制器
- TransformControls:变换物体控制器
动画刷新
动画实际上是由一帧一帧的静态图片连贯组合出来的,也就是说我们想要看到动画,需要不断的刷新页面,然后展现当前动画场景的不同状态,不断的变化的状态连贯起来就是动画了。fps 就不说了,动画是需要 requestAnimationFrame 不断执行,每次执行都去 render 当前动画的状态。
实例
现在动画的实际讲解还是很少,很多都要去翻看很多资料才能收获一点点知识,包括我自己也是一知半解,能做出实际的动画 demo,也做过几个线上的大型场景动画,但是自己要去建模设计还是不是很懂的。下面是相关这些讲解的实际例子。
var THREE = require('three');
var Stats = require('stats-js');
var OrbitControls = require('three-orbitcontrols')
require('three-fly-controls')(THREE);
var PointerLockControls = require('three-pointerlock');
var TrackballControls = require('three-trackballcontrols');
var TransformControls = require('three-transformcontrols');
var renderer;
var stats;
var width;
var height;
function initThree() {
width = document.getElementById('canvas-frame').clientWidth;
height = document.getElementById('canvas-frame').clientHeight;
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.shadowMapEnabled = true;
renderer.shadowMapSoft = true;
// 初始化容器
renderer.setSize(width, height);
document.getElementById('canvas-frame').appendChild(renderer.domElement);
renderer.setClearColor(0xFFFFFF, 1.0);
// 监控
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById('canvas-frame').appendChild(stats.domElement);
}
var camera;
function initCamera() {
camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000);
camera.position.x = 100;
camera.position.y = 200;
camera.position.z = 800;
camera.up.x = 0;
camera.up.y = 1;
camera.up.z = 0;
camera.lookAt({
x: 0,
y: 0,
z: 0
});
// camera = new THREE.OrthographicCamera(-300, 300, 300, -300, 1, 1000);
// camera.position.set(0, 200, 800);
// camera.lookAt({
// x : 0,
// y : 0,
// z : 0
// });
}
var scene;
function initScene() {
scene = new THREE.Scene();
}
var light;
function initLight() {
light = new THREE.AmbientLight(0xFF0000);
light.position.set(100, 100, 200);
scene.add(light);
}
var cube;
var mesh;
function initObject() {
var geometry = new THREE.BoxGeometry(100, 100, 100);
for (var i = 0; i < geometry.faces.length; i += 2) {
var hex = Math.random() * 0xffffff;
geometry.faces[i].color.setHex(hex);
geometry.faces[i + 1].color.setHex(hex);
}
var material = new THREE.MeshBasicMaterial({
vertexColors: THREE.FaceColors
});
mesh = new THREE.Mesh(geometry, material);
// mesh.position = new THREE.Vector3(0,0,0);
scene.add(mesh);
// 天空
var skyGeometry = new THREE.CubeGeometry(9000, 9000, 9000);
var materialArray = [];
for (var i = 0; i < 6; i++)
materialArray.push(new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('http://fe.benmu-health.com/createjsDemo/images/15.jpg'),
side: THREE.BackSide
}));
var skyMaterial = new THREE.MeshFaceMaterial(materialArray);
var skyBox = new THREE.Mesh(skyGeometry, skyMaterial);
scene.add(skyBox);
}
function initGrid() {
var helper = new THREE.GridHelper(1000, 50, 0x0000ff, 0x808080);
scene.add(helper);
}
function initActor() {
var aaa = new THREE.CubeGeometry(100, 100, 100);
var bbb = new THREE.MeshBasicMaterial({
map: texture
});
var cube = new THREE.Mesh(aaa, bbb);
cube.position.x = -200
scene.add(cube);
}
function threeStart() {
initThree();
initCamera();
initScene();
initLight();
initObject();
initGrid();
initActor();
renderer.render(scene, camera);
// 旋转控制器 类似按照某一个舞台进行旋转
// controls = new OrbitControls(camera, renderer.domElement)
// controls.enableDamping = true
// controls.dampingFactor = 1
// 飞行控制器 飞行游戏
// controls = new THREE.FlyControls(camera, renderer.domElement);
// 指针控制器 https://threejs.org/examples/misc_controls_pointerlock.html
// controls = new PointerLockControls(camera)
// scene.add(controls.getObject());
// move();
// 轨迹球控制器
// controls = new TrackballControls(camera, renderer.domElement);
// 变换物体控制器
// controls = new TransformControls(camera, renderer.domElement);
// controls.attach(mesh);
// scene.add(controls);
window.addEventListener('keydown', function (event) {
switch (event.keyCode) {
case 81: // Q
controls.setSpace(controls.space === "local" ? "world" : "local");
break;
case 17: // Ctrl
controls.setTranslationSnap(100);
controls.setRotationSnap(THREE.Math.degToRad(15));
break;
case 87: // W
controls.setMode("translate");
break;
case 69: // E
controls.setMode("rotate");
break;
case 82: // R
controls.setMode("scale");
break;
case 187:
case 107: // +, =, num+
controls.setSize(controls.size + 0.1);
break;
case 189:
case 109: // -, _, num-
controls.setSize(Math.max(controls.size - 0.1, 0.1));
break;
}
});
animation();
}
var moveForward = false;
var moveBackward = false;
var moveLeft = false;
var moveRight = false;
var canJump = false;
var raycaster;
var prevTime = performance.now();
var velocity = new THREE.Vector3();
function move() {
var havePointerLock = 'pointerLockElement' in document || 'mozPointerLockElement' in document || 'webkitPointerLockElement' in document;
document.addEventListener('pointerlockchange', changeCallback, false);
document.addEventListener('mozpointerlockchange', changeCallback, false);
document.addEventListener('webkitpointerlockchange', changeCallback, false);
function changeCallback() {
if (document.pointerLockElement === element || document.mozPointerLockElement === element || document.webkitPointerLockElement === element) {
controls.enabled = true;
} else {
controls.enabled = false;
}
}
var element = document.body;
element.addEventListener('click', () => {
element.requestPointerLock = element.requestPointerLock || element.mozRequestPointerLock || element.webkitRequestPointerLock;
element.requestPointerLock();
})
var onKeyDown = function (event) {
switch (event.keyCode) {
case 38: // up
case 87: // w
moveForward = true;
break;
case 37: // left
case 65: // a
moveLeft = true;
break;
case 40: // down
case 83: // s
moveBackward = true;
break;
case 39: // right
case 68: // d
moveRight = true;
break;
case 32: // space
if (canJump === true) velocity.y += 350;
canJump = false;
break;
}
};
var onKeyUp = function (event) {
switch (event.keyCode) {
case 38: // up
case 87: // w
moveForward = false;
break;
case 37: // left
case 65: // a
moveLeft = false;
break;
case 40: // down
case 83: // s
moveBackward = false;
break;
case 39: // right
case 68: // d
moveRight = false;
break;
}
};
document.addEventListener('keydown', onKeyDown, false);
document.addEventListener('keyup', onKeyUp, false);
raycaster = new THREE.Raycaster(new THREE.Vector3(), new THREE.Vector3(0, -1, 0), 0, 10);
}
var controls;
var xxx = 1
// 帧循环、游戏循环
function animation() {
// mesh.rotation.x +=0.01;
// scene.rotation.x += 0.01;
// scene.rotation.y += 0.01;
// scene.rotation.z += 0.01;
// mesh.rotation.y +=0.01;
// mesh.rotation.z +=0.01;
// if(mesh.position.x > 100){
// xxx = -1;
// }
// if(mesh.position.x < 0){
// xxx = 1;
// }
// mesh.position.x += xxx;
// camera.position.y += xxx;
// camera.position.x += xxx;
// camera.position.z += xxx;
// 指针控制器
// raycaster.ray.origin.copy( controls.getObject().position );
// raycaster.ray.origin.y -= 10;
// var time = performance.now();
// var delta = ( time - prevTime ) / 1000;
// velocity.x -= velocity.x * 10.0 * delta;
// velocity.z -= velocity.z * 10.0 * delta;
// velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass
// if ( moveForward ) velocity.z -= 400.0 * delta;
// if ( moveBackward ) velocity.z += 400.0 * delta;
// if ( moveLeft ) velocity.x -= 400.0 * delta;
// if ( moveRight ) velocity.x += 400.0 * delta;
// controls.getObject().translateX( velocity.x * delta );
// controls.getObject().translateY( velocity.y * delta );
// controls.getObject().translateZ( velocity.z * delta );
// if ( controls.getObject().position.y < 10 ) {
// velocity.y = 0;
// controls.getObject().position.y = 10;
// canJump = true;
// }
// prevTime = time;
controls.update();
renderer.render(scene, camera);
requestAnimationFrame(animation);
}
var texture;
// var loader = new THREE.gITFLoader();
// loader.load('http://fe.benmu-health.com/benmu-health-img/app-benmu-health/apploading.gif', (obj) => {
// texture = obj;
// debugger
// threeStart()
// })
var texture = THREE.ImageUtils.loadTexture('http://fe.benmu-health.com/benmu-health-img/app-benmu-health/apploading.gif', {}, function () {
threeStart()
});