国内某VR、AR企业笔试题分享!canvas写字效果及代码实现
本篇文章来源于一道国内做vr,ar的企业的笔试题(算是小独角兽那种)。是我今年三月实习的时候摸鱼做的一道题目。内容大概是这样的.
采用h5的canvas
1. 2d部分,画墙,注意测量部分;
2. 3d部分,根据2d部分传过来的数据,显示3d墙。建议使用:three.js引擎
最后花了一个小时搞定了。感觉还是挺有意思的。分享一下

最后我实现的效果在下图。上面有个写字的地方,你在上面写字然后three就会在下面的画布渲染出来。并且生成的3d内容支持鼠标拖拽
废话少说,我们直接开始。
在线演示地址:.gitee.io/front-css-p…
代码完整地址://…
总体思路
核心
原来的3维坐标系中 根据canvas的
轨迹 新建 3d 的 1 * 1 的方块
并且根据 canvas 的位置进行放置
1. 部分思路
先讲一下基础思路,我们首先需要实现一个可写的画布,即是 手写板之类的东西
首先是
初始化基础变量:const ctx = cvs.('2d');:获取的2D绘图上下文,这是实际进行绘图操作的对象 。 let = false;:标志变量,用于表示是否处于绘图状态。 let = null; 和 let = null;:记录绘图的起始点和结束点的变量。
添加落笔事件:cvs.('', (e) => {...});:给添加鼠标按下事件监听器。当鼠标按下时,将触发这个事件处理函数,其中:
添加笔触移动事件: cvs.('', (e) => {...});:给添加鼠标移动事件监听器。当鼠标移动时,将触发这个事件处理函数,其中:
添加提笔事件:cvs.('', (e) => {...});:给添加鼠标释放事件监听器。当鼠标释放时,将触发这个事件处理函数,其中:
抽象函数
经过上面的分析,我们就可以很轻松抽象出一个函数
可以看到,我在下面的代码中有一个 wirte函数,里面传入 你的元素就可以让这块区域变成一个手绘板并且保存变量
html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Documenttitle>
head>
<body>
<canvas id="test" width="600" height="200">canvas>
<script>
function write(dom) {
const cvs = dom;
const ctx = cvs.getContext('2d');
let isDraw = false;
let startPos = null
let endPos = null
cvs.addEventListener('mousedown', (e) => {
dataArray = []
e = e || window.event;
isDraw = true;
ctx.moveTo(e.pageX, e.pageY)
// console.log("start:",e.pageX, e.pageY)
dataArray.push([e.pageX, e.pageY])
})
cvs.addEventListener('mousemove', (e) => {
e = e || window.event;
if (isDraw) {
ctx.lineTo(e.pageX, e.pageY);
ctx.stroke();
dataArray.push([e.pageX, e.pageY])
}
})
cvs.addEventListener('mouseup', (e) => {
isDraw = false;
// console.log("end:",e.pageX, e.pageY)
console.log(dataArray)
dataArray.push([e.pageX, e.pageY])
// three.initObject(dataArray)
})
}
write(document.querySelector("#test"))
script>
body>
html>
2.思路
构造一个3d场景需要 ,scene,light,, 这些基本对象,我们可以把他们封装到一个class 类 中
构造函数:
方法:
方法:
方法:
方法:
方法:
抽象类
我们在使用的时候只需要 new () 就好了
class threeInit {
constructor(param) {
this.param = {
x: 0,
y: 0,
z: 0
}

this.initScene()
this.initLight()
this.initObject()
this.initCamera()
}
// 传入二维坐标,
initObject() {
//材质
let material = new THREE.MeshLambertMaterial({
color: "rgb(197, 81, 81)"
});
let geometry1 = new THREE.BoxGeometry(10, 10, 100); //创建一个立方体几何对象Geometry
}
initScene() {
this.scene = new THREE.Scene()
const group = new THREE.Group();
this.scene.add( group );
const helper = new THREE.GridHelper( 160, 100 );
helper.rotation.x = Math.PI / 2;
group.add( helper );
}
initLight() {
let pointLight = new THREE.PointLight(0xffffff); //创建一个白色的点光源
pointLight.position.set(0, 0, 150);
this.scene.add(pointLight);
let ambient = new THREE.AmbientLight(0xffffff, 1);
this.scene.add(ambient);
}
initCamera() {
this.width = 1280; //canvas画布宽度
this.height = 600; //canvas画布高度
let k = this.width / this.height; //canvas画布宽高比
//创建相机对象
this.camera = new THREE.PerspectiveCamera(30, k, 20, 1000);
this.camera.position.set(0, 10, 60); //设置相机位置
let v1 = new THREE.Vector3(0, 0, 0)
this.camera.lookAt(v1); //设置相机方向(指向的场景对象)
}
initRender() {
let v1 = new THREE.Vector3(0, 0, 0)
// let renderer = new THREE.WebGLRenderer();
let renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize(this.width, this.height);//设置渲染区域尺寸
// 添加控制器
let control = new OrbitControls(this.camera, renderer.domElement);
control.target = v1 //设置相机方向(指向的场景对象)
control.update();
// 渲染函数
let render = () => {
// actions[0].play();
const delta = clock.getDelta();
if ( mixer ) mixer.update( delta );
control.update();
renderer.render(this.scene, this.camera);//执行渲染操作
requestAnimationFrame(render);//请求再次执行渲染函数render,渲染下一帧
}
render();
return renderer
}
}
3. 和 联动
我们第一步我们拿到了 的 移动轨迹数据。我们只需要把 第二步的 改造成动态添加的就可以了。就是如下代码
initObject(position) {
//材质
let material = new THREE.MeshLambertMaterial({
color: "rgb(197, 81, 81)"
});
//创建一个立方体几何对象Geometry
let geometry2 = new THREE.BoxGeometry(1, 1, 10);
for(let i in position){
let mesh = new THREE.Mesh(geometry2, material); //网格模型对象Mesh
mesh.position.set(position[i][0]/10, -position[i][1]/10, 0)
this.scene.add(mesh)
}
}
有了这一步后,我们在上面的提笔事件中,我们就可以 new .(你的坐标数组)。这样子就可以进行渲染了
总结
总体来说,这段东西应该是入门难度的 和 three。做一个这种demo应该还是能够学到不少东西的
在线演示地址:.gitee.io/front-css-p…
代码完整地址://…
























