以前,由於公司一個項目須要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