天天看點

OpenHarmony - 基于 ArkUI(JS)實作圖案解鎖元件(一)

作者:楊尚曉

前言

Openharmony作為我們國内開源系統,并且以js/ets最為開發語言,完全抛棄了java。作為一名前端開發人員,也需要為咱們國産系統壯大起來,是以貢獻一份微薄之力。😄😄

項目介紹

ohos-pattern是一個解鎖功能的元件,是基于openharmony AkrUI(JS)實作的。在手機指紋解鎖還沒來臨之前,圖案解鎖是一個很熱門的功能,包括數字解鎖。不過現在也還很多應用場景中有使用到圖案解鎖和數字解鎖的功能。本文圖案解鎖僅僅隻是實作UI層面,并不涉及到加密解密的的方式。這個可以二次開發使用。

本文為第一篇,後續會更新加入數字解鎖功能,如果後續能涉及到生物識别API,還會加入指紋解鎖等三種解鎖方式。

環境說明

  • 開發工具:DevEco Studio 3.0 Beta4
  • SDK版本:3.1.6.6(API Version 8)
  • 主要元件:canvas

效果展示

OpenHarmony - 基于 ArkUI(JS)實作圖案解鎖元件(一)

元件屬性

屬性名 類型 預設值 作用
radius Number 5 密碼點半徑[5-20]
scope Number 20 熱區範圍[20-40]
point-color String #383838 密碼點内顔色
point-stroke-color String #ff9800 密碼點邊框顔色
active-color String #ff9800 激活密碼點顔色
line-color String #1a73e8 密碼線顔色
active-line-color String #04be02 激活密碼線顔色
is-show-line Boolean true 是否顯示密碼線

元件事件

屬性名 類型 傳回值 備注
result-cb Function Array 傳回密碼結果

引用元件

<element name="ohos-pattern" src="../../common/component/ohosPattern/ohosPattern"></element>
<div class="container">
  <ohos-pattern
      radius="{{radius}}"
      scope="{{scope}}"
      point-color="{{pointColor}}"
      point-stroke-color="{{pointStrokeColor}}"
      active-color="{{activeColor}}"
      line-color="{{lineColor}}"
      active-line-color="{{activeLineColor}}"
      is-show-line="{{isShowLine}}"
  ></ohos-pattern>
</div>
           

實作思路

實作思路比較簡單,使用canvas繪制九個密碼點,然後通過監聽手勢觸摸和9個密碼點的碰撞産生密碼資料

  1. 建立canvas
  2. 繪制9個密碼點
  3. 繪制可控區域(就是密碼點跟随手指一動區域)
  4. 監聽手勢

實作過程

1. 建立canvas

建立canvas元素,并且綁定touchstart,touchmove,touchend手勢事件

<div class="container">
  <canvas
      id="canvas"
      ref="canvas"
      @touchstart="onTouchStart"
      @touchmove="onTouchMove"
      @touchend="onTouchEnd"
      style="width: {{w}}px;height: {{h}}px"
  ></canvas>
</div>
           

在js中初始化canvas對象

onShow(){
    this.initCanvas();
},
// 初始化畫布
initCanvas(){
    const c = this.$refs.canvas;
    this.ctx = c.getContext('2d', { antialias: true });
},
           

2. 繪制九個密碼點

下面看個草圖

OpenHarmony - 基于 ArkUI(JS)實作圖案解鎖元件(一)

從草圖上可以看出我們的密碼點位置了,這裡使用for循環方法實作,建立3行,每行3個點。

這裡有個注意點,因為我們密碼點有一個需要跟随手勢移動的需求,需要修改point_list的資料,是以使用point_list_copy複制了一份出來,這裡使用es6的展開運算符...來實作深拷貝。

// 建立9個密碼點
createPoint(){
    let point_x = Math.ceil((this.w - this.R * 6) / 4 + this.R);
    let point_y = Math.ceil((this.h - this.R * 6) / 4 + this.R);
    for (var j = 0; j < 3; j++) {
        for (var i = 0; i < 3; i++) {
            let d = {
                x: point_x * (i + 1) + this.R * i,
                y: point_y * (j + 1) + this.R * j
            }
            this.point_list.push(d);
            this.point_list_copy = [...this.point_list];
            this.drawPoint(d);
        }
    }
},
           

下面實作将9個密碼點到畫布上

// 繪制9個密碼點到畫布上
  drawPoint(obj) {
    let {x, y} = obj;
    // 繪制外圓環
    this.ctx.beginPath();
    this.ctx.lineCap = "round"; //向線條的每個末端添加圓形線帽
    this.ctx.lineWidth = (this.R / 2);  //繪制圓的邊框
    this.ctx.strokeStyle = '#ff9800'; //繪制邊框的顔色
    this.ctx.arc(x, y, this.R, 0, 2 * Math.PI);
    this.ctx.stroke();
    this.ctx.closePath();

    // 繪制内圓
    this.ctx.beginPath();
    this.ctx.arc(x, y, this.R, 0, 2 * Math.PI);
    this.ctx.fillStyle = "#383838";
    this.ctx.fill();
    this.ctx.closePath();
  },
           

下面來看看繪制後的效果

OpenHarmony - 基于 ArkUI(JS)實作圖案解鎖元件(一)

3. 監聽手勢和密碼點的碰撞

我們寫了三個手勢事件

  • 觸摸開始 onTouchStart
  • 觸摸變化 onTouchMove
  • 觸摸結束 onTouchEnd
onTouchStart(e){
    let x = e.touches[0].localX;
    let y = e.touches[0].localY;
    this.isCollision(x, y)
  },
  onTouchMove(e){
    let x = e.touches[0].localX;
    let y = e.touches[0].localY;
    this.isCollision(x, y);
    // 每次觸摸後都需要清除畫布重新繪制
    this.ctx.clearRect(0, 0, this.w, this.h);
    this.reDraw(x, y, true);
  },
  onTouchEnd(e){
    this.ctx.clearRect(0, 0, this.w, this.h);
    this.reDraw(e.touches[0].clientX, e.touches[0].clientY, false);
    // 松開手後,記錄所有觸摸的點
    lg.log('圖案結果:',this.result)
    this.result = [];
  },
           

重繪畫布

// 重繪
  reDraw(x, y, bol) {
    for (let i in this.point_list) {
      // 重新繪制9個密碼點
      this.drawPoint(this.point_list[i]);
    }
    for (let i in this.result) {
      // 重新繪制密碼狀态
      this.drawStatus(this.result[i]);
    }

  },
           

監聽手勢觸摸是否和密碼點發生碰撞

this.scope表示熱區範圍,當觸摸點進入這個熱區範圍,說明已經跟該密碼點發生接觸碰撞了

// 是否跟9個密碼點發生接觸。
  isCollision(x, y) {
    let c_x, c_y;
    for (let i in this.point_list) {
      c_x = Math.abs(x - this.point_list[i].x);
      c_y = Math.abs(y - this.point_list[i].y);
      // 如果發生碰撞,記錄狀态
      if (c_x < this.scope && c_y < this.scope) {
        lg.warn('發生了碰撞,碰撞點是:', i)
        this.drawStatus(i)
        return
      }
    }
  },
           

當觸摸點和密碼點發生碰撞之後,需要給該密碼點繪制觸摸狀态

//  繪制觸摸到密碼點的狀态
drawStatus(index) {
  // 繪制狀态内圓
  this.ctx.beginPath();
  this.ctx.arc(this.point_list[index].x, this.point_list[index].y, (this.R / 2), 0, 2 * Math.PI);
  this.ctx.fillStyle = "#ac2dfb";
  this.ctx.fill();
  this.ctx.closePath();

  //記錄緩存碰撞的結果
  if(this.result.indexOf(index) === -1){
    this.result.push(index)
  }
},
           

下面看看效果

OpenHarmony - 基于 ArkUI(JS)實作圖案解鎖元件(一)

但是這樣還不夠,我們能還需要繪制連接配接的密碼線

4. 繪制密碼線

在重繪方法裡最頂端加入繪制密碼線方法

this.drawLine(this.result, x, y, bol);
           

繪制密碼線方法

這裡需要繪制兩條線:

  • 一條是跟随觸摸移動的線
  • 一條是已經存在兩點之間的線
drawLine(arr, x, y, bol) {
  if(arr.length === 0){
    return;
  }
  // 當存在已經兩個點的時候,兩點直線形成連線
  this.ctx.beginPath();
  for (let i in arr) {
    if (i == 0) {
      this.ctx.moveTo(this.point_list[arr[i]].x, this.point_list[arr[i]].y);
    } else {
      this.ctx.lineTo(this.point_list[arr[i]].x, this.point_list[arr[i]].y);
    }
  }
  this.ctx.strokeStyle = '#1a73e8';
  this.ctx.lineWidth = (this.R / 2);
  this.ctx.stroke();

  // 跟着手機滑動的線條
  if (bol) {
    this.ctx.beginPath();
    this.ctx.moveTo(this.point_list[arr[arr.length - 1]].x, this.point_list[arr[arr.length - 1]].y);
    this.ctx.lineTo(x, y);
    this.ctx.strokeStyle = '#04be02';
    this.ctx.lineWidth = (this.R / 2);
    this.ctx.stroke();
  }
},
           

好了,下面我們看看加上密碼線的效果

OpenHarmony - 基于 ArkUI(JS)實作圖案解鎖元件(一)

5. 實作可移動的密碼點

優化體驗,增加了密碼點跟随手勢一起移動的效果。

在重繪的頂部添加繪制可移動的密碼點方法

// 繪制跟随手勢移動的密碼點
  this.pointAn(x,y,bol);
           

r_x,r_y表示密碼點熱區範圍,在熱區範圍内可以将該密碼點變為觸摸點跟随觸摸點一起移動,當觸摸點離開了熱區之後,密碼點回到原來的中心位置。

// 繪制狀态point範圍内活動
  pointAn(x, y, bol){
    if(bol){
      if(this.result.length === 0){
        return;
      }
      let t = this.result[this.result.length - 1];
      let r_x = Math.abs(x - this.point_list_copy[t].x);
      let r_y = Math.abs(y - this.point_list_copy[t].y);
      if(r_x < this.scope && r_y < this.scope){
        this.point_list[t] = {x,y}
      } else {
        this.point_list = [...this.point_list_copy];
      }
    } else {
      this.point_list = [...this.point_list_copy];
    }
  },
           

最後來看看加上熱區的效果

OpenHarmony - 基于 ArkUI(JS)實作圖案解鎖元件(一)

到這裡,整個基本功能已經實作了。

最後再來看一下最終效果

OpenHarmony - 基于 ArkUI(JS)實作圖案解鎖元件(一)

代碼位址

ohos-pattern 基于OpenHarmony JSAPI實作圖案解鎖元件

總結

該元件整體實作邏輯都比較簡單,主要是通過手勢去繪制canvas畫布實作的,這裡需要注意的是,api version需要7以上,因為在api version 6 之前,存在canvas重繪閃屏的情況,在api 7之後修複了這個問題。相比api 6之前的版本,api8真的優化和修複了很多功能。很期待harmongOS 3.0更新,可以在真機下去體驗ets開發的應用。

更多原創内容請關注:中軟國際 HarmonyOS 技術團隊

入門到精通、技巧到案例,系統化分享HarmonyOS開發技術,歡迎投稿和訂閱,讓我們一起攜手前行共建鴻蒙生态。

繼續閱讀