使用Vue + fabric.js構(gòu)建標(biāo)注工具的細(xì)節(jié)

上篇文章大致介紹了使用Vue + fabric.js構(gòu)建標(biāo)注工具的流程退疫,本篇?jiǎng)t將其中的一些細(xì)節(jié)以及fabric的踩坑進(jìn)行補(bǔ)充

1.鼠標(biāo)從右向左畫框

承接上篇的描述,使用fabric在canvas上畫標(biāo)注框的流程主要為:

  1. 監(jiān)聽畫布的鼠標(biāo)按下mouse:down事件批什,并保存鼠標(biāo)按下時(shí)的坐標(biāo)混驰,作為標(biāo)注框的起點(diǎn)(mouseFrom)昧捷;
  2. 監(jiān)聽畫布的鼠標(biāo)移動(dòng)mouse:move事件,在鼠標(biāo)移動(dòng)過程中堤框,在canvas上繪制以第一步中的起點(diǎn)為左上角域滥,鼠標(biāo)移動(dòng)時(shí)的坐標(biāo)為右下角(mouseTo)的矩形(rect);
  3. 監(jiān)聽畫布的鼠標(biāo)抬起mouse:up事件蜈抓,鼠標(biāo)抬起時(shí)启绰,標(biāo)注框繪制完畢;
    由此得知沟使,在第二步中的標(biāo)注框的生成代碼為
rect = new fabric.Rect({
    left: mouseFrom.x,
    top: mouseFrom.y,
    width: mouseTo.x - mouseFrom.x,
    height: mouseTo.y - mouseFrom.y
    })

然而這樣設(shè)置存在一個(gè)隱患bug,當(dāng)鼠標(biāo)從左向右畫框時(shí)委可,標(biāo)注框正常,但當(dāng)鼠標(biāo)從右向左畫框時(shí),發(fā)現(xiàn)標(biāo)注框并不能如我們所期望的隨著鼠標(biāo)移動(dòng)着倾,而是一直向右畫框


291891492291361822021-09-02_16.17.20.gif

針對上面場景拾酝,一個(gè)解決方案為

在繪制框時(shí),先判斷mouseFrom.xmouseTo.x,mouseFrom.ymouseTo.y的大小卡者,以較小的那個(gè)值為標(biāo)注框的左上角的坐標(biāo)(lefttop)蒿囤,以mouseTo.x-mouseFrom.x的絕對值為標(biāo)注框的寬(width),以mouseTo.y-mouseFrom.y的絕對值為標(biāo)注框的高(height)

let x = Math.min(mouseFrom.x, mouseTo.x)
let y = Math.min(mouseFrom.y, mouseTo.y)
let width = Math.abs(mouseTo.x-mouseFrom.x)
let height = Math.abs(mouseTo.y-mouseFrom.y)
rect = new fabric.Rect({
    left: x,
    top: y,
    width: width,
    height: height
    })

以這樣的方法使得標(biāo)注框的左上定點(diǎn)是相對小的那個(gè)值崇决,雖然rect仍舊是從左畫到右材诽,但隨著鼠標(biāo)的移動(dòng),視覺上rect是隨著鼠標(biāo)從右向左畫

291891492291361822021-09-02_16.44.57.gif

2.標(biāo)注框溢出畫布

  • 繪制過程中標(biāo)注框溢出畫布
    緊接著上步所說的跟隨著鼠標(biāo)移動(dòng)繪制標(biāo)注框恒傻,當(dāng)鼠標(biāo)在畫布內(nèi)的時(shí)候脸侥,標(biāo)注框正常繪制,但是盈厘,當(dāng)鼠標(biāo)移出畫布時(shí)睁枕,mouseFrommouseTo的值仍在變化,但是溢出畫布的標(biāo)注框卻不能正常顯示沸手,因此在繪制時(shí)外遇,需要限制mouseFrommouseTo的值,使得標(biāo)注框的起點(diǎn)和終點(diǎn)均保持在畫布內(nèi)部。
limitPoint(x,y){
    if(x < 0) x = 0
    if(y < 0) y = 0
    // fabricObj為使用fabric創(chuàng)建的canvas對象罐氨,this.fabricObj.getWidth()獲取畫布的寬
    if(x > this.fabricObj.getWidth()) x = this.fabricObj.getWidth()
    // this.fabricObj.getHeight()獲取畫布的高
    if(y > this.fabricObj.getHeight()) y = this.fabricObj.getHeight()
}

[圖片上傳失敗...(image-29416e-1630578659128)]

  • 移動(dòng)標(biāo)注框過程中溢出畫布

canvas.on('object:moving', (e) => {

// 阻止對象移動(dòng)到畫布外面
      let padding = 0; // 內(nèi)容距離畫布的空白寬度,主動(dòng)設(shè)置
      var obj = e.target;
      if (obj.currentHeight > obj.canvas.height - padding * 2 ||
        obj.currentWidth > obj.canvas.width - padding * 2) {
        return;
      }
      obj.setCoords();
      if (obj.getBoundingRect().top < padding || obj.getBoundingRect().left < padding) {
        obj.top = Math.max(obj.top, obj.top - obj.getBoundingRect().top + padding);
        obj.left = Math.max(obj.left, obj.left - obj.getBoundingRect().left + padding);
      }
      if (obj.getBoundingRect().top + obj.getBoundingRect().height > obj.canvas.height - padding || obj.getBoundingRect().left + obj.getBoundingRect().width > obj.canvas.width - padding) {
        obj.top = Math.min(
          obj.top,
          obj.canvas.height - obj.getBoundingRect().height + obj.top - obj.getBoundingRect().top - padding
        );
        obj.left = Math.min(
          obj.left,
          obj.canvas.width - obj.getBoundingRect().width + obj.left - obj.getBoundingRect().left - padding
        );
      }

})

3.屏幕分辨率引起的選中狀態(tài)下的框移位

在開發(fā)過程中滩援,我遇到過這樣一個(gè)bug,起初在外接顯示器上栅隐,選中標(biāo)注框正常,但無意間拖動(dòng)到自己電腦屏幕上時(shí)玩徊,詭異的一幕發(fā)生了租悄,選中的框跟原本的標(biāo)注框不對應(yīng),再拖回到外接顯示器上囊扳,又顯示正常了

選中狀態(tài)下選中選中框的八個(gè)控制點(diǎn)沒有很好的附著在選中框上
看到這個(gè)問題授艰,著實(shí)讓人頭疼曙博,明明什么都沒動(dòng),為啥會(huì)出現(xiàn)這樣的bug?逐一對比在外接顯示器和自己電腦屏幕上console出來的被選中的標(biāo)注框的各個(gè)字段潭辈,發(fā)現(xiàn)zoomXzoomY在外接顯示器上為1,在自己電腦屏幕上為1.25澈吨,不由懷疑是zoomXzoomY這兩個(gè)字段導(dǎo)致的標(biāo)注框偏移把敢,然后去研究源碼,找到在創(chuàng)建標(biāo)注框rect時(shí)zoomXzoomY的賦值邏輯

fabric是通過drawControls()函數(shù)繪制選中狀態(tài)下的控制點(diǎn)的谅辣,其中紅線框的部分發(fā)現(xiàn)設(shè)置了transform,緊接著懷疑是canvas的getRetinalScaling()影響到了zoomXzoomY

image.png

image.png

找到getRetinalScaling()的取值函數(shù)修赞,發(fā)現(xiàn)是根據(jù)_isRetinaScaling()函數(shù)來決定取fabric.devicePixelRatio還是默認(rèn)值1,不理解fabric.devicePixelRatio是什么桑阶,就接著去找fabric.devicePixelRatio的定義
image.png

window.devicePixelRatio
image.png

到這柏副,恍然大悟勾邦,檢查自己電腦的分配率設(shè)置,果然是125%割择,與上面所述打印出來的rect的zoomXzoomY對應(yīng)眷篇,試著將分辨率改成100%,發(fā)現(xiàn)zoomXzoomY值變?yōu)?锨推,選中狀態(tài)下的控制點(diǎn)也顯示正常了

理清bug出現(xiàn)的原因后铅歼,自然而然就想到,解決此bug的關(guān)鍵點(diǎn)在于不能讓window.devicePixelRatio成為控制點(diǎn)的縮放因子换可,問題又回到了getRetinalScaling()椎椰,如果_isRetinaScaling()為false,那不管屏幕分辨率是多少,getRetinalScaling()值都取1沾鳄,控制點(diǎn)不就顯示正常了慨飘?

image.png

然后接著去找_isRetinaScaling()的取值
image.png

發(fā)現(xiàn)fabric的canvas有一個(gè)enableRetinaScaling參數(shù),默認(rèn)值為true,官網(wǎng)給出的參數(shù)含義為

image.png

單看文檔译荞,確實(shí)不知所云瓤的,但通過源碼,很好的就理解了參數(shù)的含義吞歼,感嘆一聲圈膏,文檔還是要配合源碼觀看效果更佳!

4.選中狀態(tài)下調(diào)整框的等比例縮放問題

開發(fā)完之后篙骡,產(chǎn)品提出這樣一個(gè)bug,調(diào)整標(biāo)注框拖動(dòng)上下左右四個(gè)角只能等比例縮放稽坤,產(chǎn)品期望能隨著鼠標(biāo)自由地縮放,瀏覽一遍文檔糯俗,沒有找到對應(yīng)的設(shè)置尿褪,那就只能再去源碼里面找了,尋找的過程在這里就不啰嗦了得湘,總而言之杖玲,通過自下而上地翻閱源碼,發(fā)現(xiàn)fabric的canvas有一個(gè)uniformScaling屬性控制著標(biāo)注框的等比例縮放淘正,且默認(rèn)值為true,將其設(shè)置成false后摆马,bug就迎刃而解了

[圖片上傳失敗...(image-8f96a8-1630578659128)]

5.圖片分辨率不同,標(biāo)注框的寬度設(shè)置

由于不同的圖片分辨率差異較大鸿吆,如果以同一種寬度來設(shè)置標(biāo)注框今膊,呈現(xiàn)效果相差較大,因此采取根據(jù)圖片分辨率來動(dòng)態(tài)設(shè)置標(biāo)注框?qū)挾龋╯cale為上篇文章中創(chuàng)建畫布階段伞剑,圖片寬高與畫布容器寬高的比值)

 <div id="canvax-box">
        <canvas id="label-canvas" :width="width" :height="height">
    </div>
</template>
<script>
 export default{
     methods:{
         fabricCanvas(){
                 ...
                 // 將圖片放置在外部容器中
                 let boxWidth = document.getElementById('canvas-box').offsetWidth
                 let boxHeight = document.getElementById('canvas-box').offsetHeight
                 let scaleX = boxWidth / image.width
                 let scaleY = boxHeight / image.height
                 // 確定縮放因子
                 this.scale = scaleX > scaleY ? scaleX : scaleY
                 ...
image.png
image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末斑唬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恕刘,老刑警劉巖缤谎,帶你破解...
    沈念sama閱讀 217,907評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異褐着,居然都是意外死亡坷澡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,987評論 3 395
  • 文/潘曉璐 我一進(jìn)店門含蓉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來频敛,“玉大人,你說我怎么就攤上這事馅扣≌遄” “怎么了?”我有些...
    開封第一講書人閱讀 164,298評論 0 354
  • 文/不壞的土叔 我叫張陵差油,是天一觀的道長拗军。 經(jīng)常有香客問我,道長蓄喇,這世上最難降的妖魔是什么发侵? 我笑而不...
    開封第一講書人閱讀 58,586評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮妆偏,結(jié)果婚禮上刃鳄,老公的妹妹穿的比我還像新娘。我一直安慰自己钱骂,他們只是感情好叔锐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,633評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著罐柳,像睡著了一般掌腰。 火紅的嫁衣襯著肌膚如雪狰住。 梳的紋絲不亂的頭發(fā)上张吉,一...
    開封第一講書人閱讀 51,488評論 1 302
  • 那天,我揣著相機(jī)與錄音催植,去河邊找鬼肮蛹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛创南,可吹牛的內(nèi)容都是我干的伦忠。 我是一名探鬼主播,決...
    沈念sama閱讀 40,275評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼稿辙,長吁一口氣:“原來是場噩夢啊……” “哼昆码!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,176評論 0 276
  • 序言:老撾萬榮一對情侶失蹤赋咽,失蹤者是張志新(化名)和其女友劉穎旧噪,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體脓匿,經(jīng)...
    沈念sama閱讀 45,619評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淘钟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,819評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陪毡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片米母。...
    茶點(diǎn)故事閱讀 39,932評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖毡琉,靈堂內(nèi)的尸體忽然破棺而出铁瞒,到底是詐尸還是另有隱情,我是刑警寧澤绊起,帶...
    沈念sama閱讀 35,655評論 5 346
  • 正文 年R本政府宣布精拟,位于F島的核電站,受9級特大地震影響虱歪,放射性物質(zhì)發(fā)生泄漏蜂绎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,265評論 3 329
  • 文/蒙蒙 一笋鄙、第九天 我趴在偏房一處隱蔽的房頂上張望师枣。 院中可真熱鬧,春花似錦萧落、人聲如沸践美。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,871評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽陨倡。三九已至,卻和暖如春许布,著一層夾襖步出監(jiān)牢的瞬間兴革,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,994評論 1 269
  • 我被黑心中介騙來泰國打工蜜唾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留杂曲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,095評論 3 370
  • 正文 我出身青樓袁余,卻偏偏與公主長得像擎勘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子颖榜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,884評論 2 354

推薦閱讀更多精彩內(nèi)容