色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

【開發者說】開發案例:使用canvas實現圖表系列之折線圖

HarmonyOS開發者 ? 來源:未知 ? 2023-12-13 16:05 ? 次閱讀
# 開發者說 #

【開發者說】欄目是為HarmonyOS開發者提供的展示和分享平臺,在這里,大家可以發表自己的技術洞察和見解,也可以展示自己的開發心得和成果。

歡迎大家積極投稿,后臺回復【投稿】,即可獲得投稿渠道。期待你們的分享~

由于對HarmonyOS的興趣與開發需求,我已經打卡學習ArkTS語言28天了。在模擬開發歷史項目的時候,會經常需要使用到圖表這類樣式展示,我決定結合之前學習的canvas繪畫知識,自己寫一個折線圖組件,希望對各位開發者有所幫助。

功能結構

實現一個公共組件的時候,首先分析一下大概的實現結構以及開發思路,方便我們少走彎路,也可以使組件更加容易拓展,維護性更強。然后我會把功能逐個拆開來講,這樣大家才能學習到更詳細的內容。下面簡單闡述下折線圖組件的功能結構:

wKgZomV5ZvGAGzrqAABM_95KNDw782.png以上是基礎的功能結構框架,包含一些比較簡單的基礎功能,后續還有點擊觸發、動畫等功能也會規劃進去。這一期我們先實現上面這些基礎的功能,后續再慢慢拓展。

公共屬性

一個組件肯定會有一些公共的屬性作為動態參數,便于組件之間的信息傳遞,我們分別講解一下五個公共屬性的作用:
  1. 畫布的寬度(cWidth)和高度(cHeight),這個是最基本的。但是我這里控制是非必傳,默認值都是100%就可以了。

  2. 畫布的內部留白間距(cSpace)。主要是用來控制內容區與畫布外框的距離,避免繪畫的內容被截掉。

  3. 字體大小(fontSize)。主要是來控制整個繪畫內容的字體大小,全局性,避免每個小功能都需要傳字體大小。

  4. 字體顏色(color)。與字體大小的功能一致。

  5. 圖表數據(data)。用來存儲圖表內容的數組,其中name與value是必傳的。

以下是具體的代碼:


	
 // 圖表數據的特征接口
interface interface_data {
  name: string | number;
  value: string | number;
  [key: string]: any;
}


// 圖表的特征接口
interface interface_option {
  cWidth?: string | number,
  cHeight?: string | number,
  fontSize?: string | number,
  color?: string,
  cSpace?: number,
  data?: interface_data[]
}


// option 默認值
const def_option: interface_option = {
  cWidth: '100%',
  cHeight: '100%',
  fontSize: 10,
  color: '#333',
  cSpace: 20,
  data: []
}


@Component
export struct McLineChart {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  @State options: interface_option = {}
  aboutToAppear() {
    this.options = Object.assign({}, def_option, this.options)
  }
  build() {
    Canvas(this.context)
      .width(this.options.cWidth)
      .height(this.options.cHeight)
      .onReady(() => {
        
      })
  }
}
(左右滑動查看更多)

繪畫坐標軸

繪畫圖表內容區部分,首先是繪畫坐標軸,坐標軸分為X軸跟Y軸,我們要先開始畫Y軸,原因是:y軸上要顯示文本標簽,如果一開始沒有得到文本標簽對應的寬度最大值,那么Y軸跟X軸的起點坐標就會有偏差,會導致繪畫全部錯位,下圖是完整的坐標軸的效果。

wKgZomV5ZvGAbMiTAAAlHkuEnbc035.png

1.繪畫Y軸

Y軸整體是由軸線、分割線、刻度線、文本標簽四個部分組成的,四個部分都有先后關系,而且包含一定的算法邏輯,下面簡單用一個概念圖進行講解。

wKgZomV5ZvGASmFWAAA3pYDyH9Y940.png

首先用500*500的矩形作為我們這次的畫布,我們可以在圖上看到Y軸整體包含了文本標簽、Y軸線、分割線、刻度線四個部分。而canvas繪畫基本都是通過坐標來定位的,Y軸整體的四個部分的起點與結束坐標都互相有關系,甚至需要把內部間距、分割間距、y軸線高度、文本最大的寬度四個屬性計算在內。以上是概念與思路,接下來我們逐一講解代碼:

1、計算得到文本最長寬度(maxNameW),我們可以從圖中看到,不論是y軸線、刻度線還是分割線的起點坐標都是需要內容間距、文本標簽、文本標簽與分割線間隔相加計算得到,而為了保持對齊,所以我們需要計算出文本最長寬度。而y軸的文本一般都是數據(data)對應的數值,所以我們需要得到傳入數據(data)中的最大值。然后講最大值分割成五等分。以下就是計算獲取最大文本寬度的代碼,部分邏輯我也會寫在代碼上:


	
build() {
    Canvas(this.context)
      .width(this.options.cWidth)
      .height(this.options.cHeight)
      .backgroundColor(this.options.backgroundColor)
      .onReady(() => {
        const values: number[] = this.options.data.map((item) => Number(item.value || 0))
        const maxValue = Math.max(...values)
        let maxNameW = 0
        let cSpiltNum = 5 // 分割等分
        let cSpiltVal = maxValue / cSpiltNum // 計算分割間距
        for(var i = 0; i <= this.options.data.length; i++){
          // 用最大值除于分割等分得到每一個文本的間隔值,而每一次遍歷用間隔值乘于i就能得到每個刻度對應的數值了,計算得到得知需要保留整數且轉成字符串
          const text = (cSpiltVal * i).toFixed(0)
          const textWidth = this.context.measureText(text).width; // 獲取文字的長度
          maxNameW = textWidth > maxNameW ? textWidth : maxNameW // 每次進行最大值的匹配
        }
      })
}

左右滑動查看更多

2、繪畫文本標簽,我們可以從圖中看到文本標簽的x坐標只跟內部間距有關,而且我們從上面代碼就已經得到每個刻度的分割間距了,從而可以得到每個文本的y軸。


	
.onReady(() => {
   ....
   for(var i = 0; i <= this.options.data.length; i++){
     ...
     // 繪畫文本標簽
     this.context.fillText(text, this.options.cSpace, cSpiltVal * (this.options.data.length - i) + this.options.cSpace , 0);
   }
})

左右滑動查看更多

3、繪畫刻度線。我們可以從概念圖得到,刻度線的起點x坐標算法是:內部間距(cSpace)加最長文本寬度(maxNameW)加上文本與刻度線的間距,起點y坐標則跟文本一樣,通過分割間距與下角標的關系得到每個刻度的y坐標;而終點x坐標則是刻度線的長度,終點y坐標則跟起點的y坐標一樣,我設置默認長度是5,這樣就能得到我們的刻度線了。代碼如下:


	
.onReady(() => {
  ....
  const length = this.options.data.length
  for(var i = 0; i <= length; i++){
    ...
  }
  // 上面是獲取最長文本寬度的代碼
  // 畫線的方法
  function drawLine(x, y, X, Y){
    this.context.beginPath();
    this.context.moveTo(x, y);
    this.context.lineTo(X, Y);
    this.context.stroke();
    this.context.closePath();
  }
  for(var i = 0; i <= length; i++){
    const item = this.options.data[i]
    // 繪畫文本標簽
    ctx.fillText(text, this.options.cSpace,  cSpiltVal * (this.data.length - i) + this.options.cSpace, 0);
    // 內部間距+文本長度
    const scaleX = this.options.cSpace + maxNameW
    // 通過數據最大值算出等分間隔,從而計算出每一個的終點坐標
    const scaleY = cSpiltVal * (length - i) + this.options.cSpace
    // 這里的5就是我設置文本跟刻度線的間隔與刻度線的長度
    drawLine(scaleX, scaleY, scaleX + 5 + 5, scaleY);
  }
})

左右滑動查看更多

4、繪畫y軸線。繼續分析概覽圖,從圖中我們可以得到:y軸線的起點x坐標的算法是:內部間距(cSpace)加最長文本寬度(maxNameW)加上文本與刻度線的間距以及刻度線長度,起點y坐標則是內部上間距;而終點x坐標與起點x坐標相同,終點y坐標算法是:畫布高度減去上下兩邊的內部間距。通過以上計算關系就能繪畫出y軸線了。代碼如下:


	
.onReady(() => {
  ...
  // 上面是繪畫其他組成部分代碼
   const startX = this.options.cSpace + maxNameW + 5 + 5
   const startY = this.options.cSpace
   const endX = startX
   const endY = this.context.height - (this.options.cSpace * 2)
   drawLine(startX, startY, endX, endY); // 繪畫y軸
})

左右滑動查看更多

5、繪畫分割線。其實從圖中可以看出分割線與刻度線差不多,起點x坐標算法是:在刻度線起點x坐標基礎上加刻度線長度;起點y軸與刻度線相同。而終點的x坐標算法:畫布寬度減去起點x坐標;終點的y坐標與起點的y坐標相同。具體代碼如下:


	
.onReady(() => {
  ....
  // 上面是獲取最長文本寬度的代碼
  for(var i = 0; i <= length; i++){
    const item = this.options.data[i]
    // 繪畫文本標簽跟刻度
    ...
    // 繪畫分割線
    const splitX = scaleX + 5 + 5
    const splitY = scaleY
    drawLine(splitX, splitY, this.context.width - splitX - this.options.cSpace, splitY);
  }
})

左右滑動查看更多

2.繪畫X軸

繪畫完Y軸之后,我們接著繪畫X軸, X軸與Y軸繪畫邏輯一致,只是方向不同而已。具體的算法就不一一詳解,可以參考一下概念圖。

wKgZomV5ZvGAABuzAABKE6UgmGg938.png

而與繪畫Y軸不一致的在于:

  1. 最長對象不一樣。Y軸最長是文本寬度;而X軸需要獲取的最長是文本高度。

  2. 間隔分割數不一樣。Y軸是自定義的分割數;而X軸分割線是實際數據的長度。

  3. 分割間距長度算法不一樣。Y軸算法是用數據最大值處于自定義的分割數;而X軸算法是用畫布寬度減去(左右兩邊的內部間隙以及Y軸寬度(文本最長寬度加上刻度線寬度)),再除去數據的長度,得到每個間隔的長度。

除了上面三點需要注意的,其他的就是調換一下計算的位置。X軸整體的代碼如下:


	
.onReady(() => {
  const cSpace = this.options.cSpace
  // 上面是繪制y軸的代碼
  ....
  // 繪制x軸
  // 獲取每個分割線的間距:this.context.width - 20為x軸的長度
  let xSplitSpacing = parseInt(String((this.context.width - cSpace * 2 - maxNameW) / this.options.data.length))
  let x = 0;
  for(var i = 0; i <= this.options.data.length; i++){
    // 繪畫分割線
    x = xSplitSpacing * (i + 1) // 計算每個數值的x坐標值
    this.drawLine(x + cSpace + maxNameW, this.context.height - cSpace, x + cSpace + maxNameW, cSpace);
    // 繪制刻度
    this.drawLine(x + cSpace + maxNameW, this.context.height - cSpace, x + cSpace + maxNameW, this.context.height - cSpace);
    // 繪制文字刻度標簽
    const text = this.options.data[i].name
    const textWidth = this.context.measureText(text).width; // 獲取文字的長度
    // 這里文本的x坐標需要減去本身文本寬度的一半,這樣才能居中顯示, y坐標這是畫布高度減去內部間距即可
    this.context.fillText(text, x + cSpace + maxNameW - textWidth / 2, this.context.height - cSpace, 0);
  }
this.context.save();
  this.context.rotate(-Math.PI/2);
  this.context.restore();
})
左右滑動查看更多

繪畫折線區

繪畫完坐標軸之后,就可以來繪畫折線區的內容了。也是整個畫布重點的部分。折線區分為三個部分:繪畫折線、繪畫標點、繪畫文本。

1.繪畫折線

wKgZomV5ZvGAa7PUAABP1Dqu08o709.png

從上面的圖可以看出折線直接就是把實際數據的數值轉成x跟y坐標,再通過連線連接起來。而每一個轉折點的x坐標算法跟x軸的刻度或者文本是一樣的,而y坐標是實際數值通過一定算法轉成我們需要的高度。x坐標我們已經獲取了,只要是攻克我們的y坐標即可??梢酝ㄟ^圖來觀察一下在畫布中與實際數據的關系:

wKgZomV5ZvKAKO0ZAABZX6Uc3Q8134.png

首先Y軸的高度代表的是實際數據的最大值,這個我們繪畫Y軸的時候就得到的結果,那我們則可以算出Y軸高度與實際數據的縮放倍數(scale),而折線的的每個y坐標對應的也是實際數值,需要把實際數值轉換成畫布中高度,那么就用實際數值乘與剛剛得到的縮放倍數(scale)就能得到轉化后的高度了。

雖然我們已經得到每個轉折點縮放后的高度,但是如果要跟Y軸坐標一一對應的y坐標的畫,還需要用畫布的高度減去下邊內部高度加x軸高度,再減去縮放后的實際高度。這樣算出來的才是我們想要的y坐標值,大概算法關系已經知道了,以下是最終代碼:
.onReady(() => {
  ...
  // 上面是繪制x軸跟y軸的代碼
  // 繪畫折線
  const ySacle = (this.context.height - cSpace *2) / maxValue // 計算出y軸與實際最大值的縮放倍數
  //連線
  this.context.beginPath();
  for(var i=0; i< this.options.data.length; i++){
    const dotVal = String(this.options.data[i].value);
    const x = xSplitSpacing * (i + 1) + cSpace + maxNameW // 計算每個數值的x坐標值
    const y = this.context.height - cSpace - parseInt(dotVal * ySacle); // 畫布的高度減去下邊內部高度加x軸高度,再減去縮放后的實際高度
    if(i==0){
      // 第一個作為起點
      this.context.moveTo( x, y );
    }else{
      this.context.lineTo( x, y );
    }
  }
  ctx.stroke();
})

左右滑動查看更多

2.繪畫標點、文本標簽

畫完折線我們基本能得到很多東西,比如折線上每個轉折點的x跟y坐標值。這樣對我們繪畫標點與文本標簽就很方便了:

.onReady(() => {
  ...
  // 上面是繪制x軸跟y軸的代碼
  // 繪畫折線
  const ySacle = (this.context.height - cSpace *2) / maxValue // 計算出y軸與實際最大值的縮放倍數
  this.context.beginPath();
  for(var i=0; i< this.options.data.length; i++){
    // 繪畫折線代碼
    ...
    // 繪制標點
    drawArc(x, y);
    // 繪制文本標簽
    const textWidth = this.context.measureText(dotVal).width; // 獲取文字的長度
    const textHeight = this.context.measureText(dotVal).height; // 獲取文字的長度
    this.context.fillText(dotVal, x - textWidth / 2, y - textHeight / 2); // 文字
  }


  function drawArc( x, y ){
    this.context.beginPath();
    this.context.arc( x, y, 3, 0, Math.PI*2 );
    this.context.fill();
    this.context.closePath();
  }
  this.context.stroke();
})

左右滑動查看更多

最終效果如下:wKgZomV5ZvKAcrV8AABTPnN5Lfc164.png

總結

以上是本次技術分析,希望能對大家有所啟發,也祝愿各位開發者能開發出理想的效果,后續我們會把chart相關系列的組件封裝到組件庫發布到市場上,這樣可以直接開箱即用了。敬請期待吧,后續還有很多技術的分享,不要錯過!


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • HarmonyOS
    +關注

    關注

    79

    文章

    1977

    瀏覽量

    30255

原文標題:【開發者說】開發案例:使用canvas實現圖表系列之折線圖

文章出處:【微信號:HarmonyOS_Dev,微信公眾號:HarmonyOS開發者】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    云端AI開發者工具怎么用

    云端AI開發者工具通常包括代碼編輯器、模型訓練平臺、自動化測試工具、代碼管理工具等。這些工具不僅降低了AI開發的門檻,還極大地提高了開發效率和模型性能。下面,AI部落小編為您介紹云端AI開發者
    的頭像 發表于 12-05 13:31 ?137次閱讀

    KaihongOS 4.1.2開發者預覽版正式上線,誠邀開發者免費試用!

    深開鴻在2024開放原子開源生態大會上正式宣布KaihongOS4.1.2開發者預覽版全面上線,并向全球開發者開放免費下載。作為KaihongOS不斷創新與發展的重要里程碑,此次預覽版為開發者提供了
    的頭像 發表于 09-28 08:07 ?344次閱讀
    KaihongOS 4.1.2<b class='flag-5'>開發者</b>預覽版正式上線,誠邀<b class='flag-5'>開發者</b>免費試用!

    KaihongOS 4.1.2開發者預覽版正式上線,誠邀開發者免費試用!

    今日,深開鴻在2024開放原子開源生態大會上正式宣布KaihongOS 4.1.2開發者預覽版全面上線,并向全球開發者開放免費下載。作為KaihongOS不斷創新與發展的重要里程碑,此次預覽版為
    的頭像 發表于 09-26 15:59 ?468次閱讀

    開發者空間實踐指導:基于 3 大 PaaS 主流服務輕松實現文字轉換語音

    【摘要】 基于 CodeArts API 設計語音合成接口,基于 API Explorer 調試接口,并利用 CodeArts IDE 實現數據流轉換為音頻 案例簡介 開發者將在云主機中
    的頭像 發表于 09-13 16:25 ?471次閱讀
    <b class='flag-5'>開發者</b>空間實踐指導:基于 3 大 PaaS 主流服務輕松<b class='flag-5'>實現</b>文字轉換語音

    HarmonyOS開發案例:【一次開發,多端部署(視頻應用)】

    隨著智能設備類型的不斷豐富,用戶可以在不同的設備上享受同樣的服務,但由于設備形態不盡相同,開發者往往需要針對具體設備修改或重構代碼,以實現功能完整性和界面美觀性的統一。OpenHarmony為開發者
    的頭像 發表于 05-11 15:41 ?1480次閱讀
    HarmonyOS<b class='flag-5'>開發案</b>例:【一次<b class='flag-5'>開發</b>,多端部署(視頻應用)】

    OpenHarmony南向開發案例:【智能體重秤】

    通過數字管家應用監測體重秤上報數據,獲得當前測量到的體重,身高,并在應用端形成一段時間內記錄的體重值,以折線圖的形式表現出來,根據計算的BMI值來提醒當前身體健康狀態,推送健康小知識。
    的頭像 發表于 04-17 11:12 ?634次閱讀
    OpenHarmony南向<b class='flag-5'>開發案</b>例:【智能體重秤】

    3568F-視頻開發案

    3568F-視頻開發案
    的頭像 發表于 04-12 13:51 ?906次閱讀
    3568F-視頻<b class='flag-5'>開發案</b>例

    鴻蒙ArkTS開始實例:【canvas實現簽名板功能】

    使用ArkTS中的canvas實現簽名板的功能,canvas畫布大家都很熟悉,我們會用它經常實現一些畫板或者圖表、表格之類的功能。
    的頭像 發表于 04-08 10:10 ?955次閱讀
    鴻蒙ArkTS開始實例:【<b class='flag-5'>canvas</b><b class='flag-5'>實現</b>簽名板功能】

    機智云開發者中心:讓移動APP應用開發更智能化

    在移動應用市場的蓬勃發展中,開發者們面臨著不斷提高效率和創新的挑戰。為了幫助開發者更好地實現移動應用開發的目標,機智云物聯網的新版開發者中心
    的頭像 發表于 03-26 16:45 ?346次閱讀
    機智云<b class='flag-5'>開發者</b>中心:讓移動APP應用<b class='flag-5'>開發</b>更智能化

    華為宣布HarmonyOS NEXT鴻蒙星河版開發者預覽面向開發者開放申請

    華為宣布HarmonyOS NEXT鴻蒙星河版開發者預覽面向開發者開放申請,這意味著鴻蒙生態進入第二階段,將加速千行百業的應用鴻蒙化。
    的頭像 發表于 01-29 16:42 ?1430次閱讀
    華為宣布HarmonyOS NEXT鴻蒙星河版<b class='flag-5'>開發者</b>預覽面向<b class='flag-5'>開發者</b>開放申請

    TLT507-視頻開發案

    TLT507-視頻開發案
    的頭像 發表于 01-26 10:50 ?992次閱讀
    TLT507-視頻<b class='flag-5'>開發案</b>例

    TLT507-Linux-RT應用開發案

    TLT507-Linux-RT應用開發案
    的頭像 發表于 01-26 09:46 ?710次閱讀
    TLT507-Linux-RT應用<b class='flag-5'>開發案</b>例

    TL3588-視頻開發案

    TL3588-視頻開發案
    的頭像 發表于 01-24 16:29 ?685次閱讀
    TL3588-視頻<b class='flag-5'>開發案</b>例

    TL3588-NPU開發案

    TL3588-NPU開發案
    的頭像 發表于 01-24 15:22 ?633次閱讀
    TL3588-NPU<b class='flag-5'>開發案</b>例

    RK3568---NPU開發案

    RK3568---NPU開發案
    的頭像 發表于 01-19 13:50 ?956次閱讀
    RK3568---NPU<b class='flag-5'>開發案</b>例
    主站蜘蛛池模板: 中文字幕s级优女区| 都市妖奇谈有声| 国产最新地址| 花蝴蝶在线直播观看| 亚洲精品另类有吗中文字幕| 久久国产一区二区三区| 2021扫黑风暴在线观看免费完整版| 免费成年人在线观看视频| 超碰在线视频地址| 亚洲精品成人a| 欧美成人精品高清在线观看| 囯产精品久久久久久久久免费蜜桃 | 综合网伊人| 日韩高清一区二区三区不卡| 国产精品午夜小视频观看| 在线观看视频一区| 日韩亚洲欧美中文在线| 九九久久国产精品免费热6| 爱穿丝袜的麻麻3d漫画免费| 亚洲绝美精品一区二区| 青青app| 久久精品AV无码亚洲色欲| 东京热无码中文字幕av专区| 伊人久久国产精品| 视频专区亚洲欧美日韩| 美女张开大腿| 国产亚洲精品AV麻豆狂野| JIZZ学生13| 一本道久在线综合色色| 射死你天天日| 嗯好大好猛皇上好深用力| 91精品国产免费入口| 午夜福利不卡片在线播放免费| 毛片网站网址| 国产香蕉视频在线播放| 饱满奶大30p| 最近的中文字幕免费完整| 亚洲AV 中文字幕 国产 欧美| 欧洲videosdesrxotv| 久久视频在线视频观看天天看视频 | 中文中幕无码亚洲视频|