以前,由于公司一个项目须要3D的楼层,因而入了3D的坑。后面封装了墙的函数等等,可是经过PS去量设计图,获取墙的点数据,把我本身搞的真的恶心了。项目完成后,随着本身技术能力的提高,忽然间想到为啥,我不用canvas照着设计稿画线,而后获取canvas上面的点数据?因而诞生了我这个标题!!!
canvas,vue,three.jshtml
目前的效果,在画布中画线,而后保存点数据,再在3D中经过点数据生成墙;vue
canvas部分的功能:鼠标画线,采集线2端的点数据。git
<canvas v-show="canvasShow" id="canvas" ref= "canvas" @mousedown="canvsClick($event)" @mousemove="drawMove($event)" @mouseup="drawEnd($event)"></canvas>复制代码
参数github
ctx:null, //储存画笔
lines:[], //储存点
linesNow:{ //当前线(正在画的)
sX:0, //鼠标按下点的横坐标
sY:0, //鼠标按下点的纵坐标
eX:0, //鼠标松开点的横坐标
eY:0 //鼠标松开点的纵坐标
},
isLine :true, //后面可能会添加模型等等;目前只有‘线’
Down:false, //是否是鼠标按下的状态
复制代码
初始化canvas canvas
//canvas渲染 初始化canvas
render:function(){
var myCanvas = this.$refs.canvas;
myCanvas.width = window.innerWidth;
myCanvas.height = window.innerHeight;
this.ctx = myCanvas.getContext("2d");
},
复制代码
鼠标事件,在this.lines中储存点数据数组
//鼠标按下
canvsClick:function($event){
this.Down = true;
//假设默认是画线的状态
if(this.isLine){
this.linesNow.sX = $event.pageX;
this.linesNow.sY = $event.pageY;
this.linesNow.eX = $event.pageX;
this.linesNow.eY = $event.pageY;
}
},
//鼠标移动
drawMove:function(e){
if(this.Down){
this.linesNow.eX = e.pageX;
this.linesNow.eY = e.pageY;
}else{
return;
}
},
//鼠标松开
drawEnd:function(){
this.Down = false;
//一条线画完,将点数据存入 lines数组,清空linesNow;
//若是几乎没画, 不添加点(初始点和结束点距离太近)
if(Math.abs(this.linesNow.eX-this.linesNow.sX)<5 && Math.abs(this.linesNow.eY-this.linesNow.sY)<5 ){
}else{
this.lines.push(JSON.parse(JSON.stringify(this.linesNow)));
}
this.linesNow={
sX:0,
sY:0,
eX:0,
eY:0
};
},
复制代码
绘制canvas中的线条bash
//循环渲染(鼠标画线的动画)
animated:function(){
//在canvas环境下
if(!this.canvasShow){
return;
}
//清除画布,重绘
this.ctx.clearRect(0,0,this.$refs.canvas.width,this.$refs.canvas.height);
this.drawLines();
//循环
requestAnimationFrame(this.animated);
},
//画线(历史的线+实时的线)
drawLines:function(){
var _this= this;
if(this.Down){
this.drawLine(this.linesNow);
}
this.lines.map(function(v,i){
_this.drawLine(v);
});
},
//画线函数封装
drawLine:function(lines){
this.ctx.beginPath();//开始路径
this.ctx.moveTo(lines.sX,lines.sY);//定义路径起始点
this.ctx.lineTo(lines.eX,lines.eY);//路径的去向
this.ctx.closePath();
this.ctx.stroke();
},复制代码
初始化3d渲染器及相关设置(场景,相机,光)app
//初始换3D渲染器
render2:function(){
this.renderer = new THREE.WebGLRenderer({antialias: true});
let renderer = this.renderer;
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0xeeeeee);
renderer.shadowMap.enabled = true;
//告诉渲染器须要阴影效果
document.getElementById("app").appendChild(renderer.domElement);
//加载3D基础环境
this.initCamera();
this.initScene();
this.initLight();
this.initStats();
//drawBoxs画墙
this.drawBoxs();
},
//初始化相机
initCamera:function() {
this.camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 2000);
this.camera.position.set(0, 450, 800 );
this.camera.lookAt(0,0,0);
},
//初始化场景
initScene:function() {
this.scene = new THREE.Scene();
this.scene.background = new THREE.Color( 0xa0a0a0 );
this.scene.fog = new THREE.Fog( 0xa0a0a0, 5, 1600 );
},
//初始化光
initLight:function() {
this.scene.add(new THREE.AmbientLight(0x444444));
this.light = new THREE.DirectionalLight(0xffffff);
let light = this.light;
light.position.set(0, 200, 100 );
light.castShadow = true;
light.shadow.camera.top = 10;
light.shadow.camera.bottom = -10;
light.shadow.camera.left = -10;
light.shadow.camera.right = 10;
//告诉平行光须要开启阴影投射
light.castShadow = true;
this.scene.add(light);
},
//初始化(性能监测)
initStats:function() {
this.stats = new Stats();
document.body.appendChild(this.stats.dom);
},
复制代码
物体及基础场景(地板等)dom
drawBoxs:function(){
//基础3D环境
this.drawBaseBg();
var _this = this;
this.lines.map(function(v,i){
_this.drawWall(v);
})
},
//画墙
drawWall:function(objs){
//长度
let lens =Math.sqrt(Math.pow((Number(objs.eY) - Number(objs.sY)),2) + Math.pow((Number(objs.eX) - Number(objs.sX)),2) );
//位置
let posx = (objs.eX+objs.sX)/2;
let posz = (objs.eY+objs.sY)/2;
//旋转角度
let rotate =-Math.atan2((objs.eY-objs.sY),(objs.eX-objs.sX));
console.log(Math.atan2((objs.eY-objs.sY),(objs.eX-objs.sX)))
let box = new THREE.CubeGeometry(lens,this.wallHei,this.wallWid);
var material = new THREE.MeshBasicMaterial({color:0xcccccc});
var mesh = new THREE.Mesh(box,material);
mesh.position.set(posx,this.wallHei/2,posz);
mesh.rotation.y = rotate;
this.scene.add(mesh);
},
//辅助工具 地板
drawBaseBg:function(){
//辅助工具
var helper = new THREE.AxesHelper(200);
this.scene.add(helper);
// 地板
var mesh = new THREE.Mesh( new THREE.PlaneBufferGeometry( 4000, 4000 ), new THREE.MeshPhongMaterial( { color: 0xffffff, depthWrite: false } ) );
mesh.rotation.x = - Math.PI / 2;
mesh.receiveShadow = true;
this.scene.add( mesh );
//添加地板割线
var grid = new THREE.GridHelper( 2000, 50, 0x000000, 0x000000 );
grid.material.opacity = 0.2;
grid.material.transparent = true;
this.scene.add( grid );
},复制代码
循环渲染3d场景函数
//循环渲染3D
animate2:function() {
if(this.canvasShow){
return;
}
//更新性能插件
this.stats.update();
this.renderer.render(this.scene, this.camera);
requestAnimationFrame(this.animate2);
},
复制代码
canvas的坐标是以左上角为默认(0,0)点。同时canvas宽高默认300*300。
three.js 中的坐标和camera相机有关。通常是y轴向上的3D坐标。(下图第一个)
因而,在点击生成3D的时候须要对咱们存的点数据进行处理。固然也能够对canvas坐标系进行处理。(我采起的是处理点数据)(因为,我three.js内的参数设置的和canvas生成的点的数据差很少,因而我就省略了一步点数据缩放的步骤)
//按钮,生成3d;(同时启动3d的循环渲染)
create3d:function(){
var _this = this;
let res =this.linesC(this.lines);
res.map(function(v,i){
_this.drawWall(v);
});
this.animate2();
},
//转换中心后的坐标
linesC:function(lines){
let res = [];
let wWid = window.innerWidth,wHei = window.innerHeight;
let _this = this;
lines.map(function(v,i){
let obj = {
sX:Number(v.sX)-wWid/2,
sY:Number(v.sY)-wHei/2,
eX:Number(v.eX)-wWid/2,
eY:Number(v.eY)-wHei/2,
}
res.push(obj);
});
return res;
},
复制代码
其实,我此次只是实现了基础的线生成墙的功能,比较low,你们将就看。
下次,我准备把‘线的选中’,‘线的移动’,‘线的删除’,‘3D墙的集成’等功能加进去。
github地址:https://github.com/baiDog/something/blob/master/pages/2019_3/2019_3_19.html