我想作個3D編輯器(一) 畫線成牆

以前,由於公司一個項目須要3D的樓層,因而入了3D的坑。後面封裝了牆的函數等等,可是經過PS去量設計圖,獲取牆的點數據,把我本身搞的真的噁心了。項目完成後,隨着本身技術能力的提高,忽然間想到爲啥,我不用canvas照着設計稿畫線,而後獲取canvas上面的點數據?因而誕生了我這個標題!!!

1、使用到的技術

canvas,vue,three.jshtml

2、大概效果


目前的效果,在畫布中畫線,而後保存點數據,再在3D中經過點數據生成牆;vue

3、實現

(1)canvas部分;

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();
},複製代碼

(2)three.js部分

初始化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);
},
複製代碼

(3)點擊生成3D操做,及座標轉化

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

相關文章
相關標籤/搜索