【圖解鴻蒙】使用繪圖組件Canvas繪制心率曲線圖
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz
一、運(yùn)行效果
在頁(yè)面中顯示頁(yè)面標(biāo)題、心率曲線圖、心率最大值及其圖標(biāo)、心率最小值及其圖標(biāo)、心率在每分鐘內(nèi)的平均次數(shù)。如下圖所示:
二、實(shí)現(xiàn)思路
在頁(yè)面的生命周期事件函數(shù)onInit()中,隨機(jī)生成若干個(gè)指定范圍內(nèi)的整數(shù),以作為所有的心率數(shù)據(jù)。根據(jù)隨機(jī)生成的整數(shù)統(tǒng)計(jì)所有心率的最大值、最小值和平均值,并通過(guò)動(dòng)態(tài)綁定的方式將其顯示在頁(yè)面中。使用組件chart繪制心率曲線圖。通過(guò)動(dòng)態(tài)綁定的方式指定組件chart中屬性options和datasets的值,以對(duì)圖形的參數(shù)進(jìn)行設(shè)置。
三、代碼詳解
打開文件index.hml。
將組件text中顯示的頁(yè)面標(biāo)題修改為:心率曲線,并在其外層嵌套一個(gè)組件div,以便對(duì)其樣式進(jìn)行設(shè)置。將該組件div的屬性class的值設(shè)置為"title-container"。
在頁(yè)面標(biāo)題的下方添加一個(gè)組件div以顯示心率曲線圖,并將屬性class的值設(shè)置為"chart"。
在心率曲線圖的下方添加一個(gè)組件list,以顯示心率的最大值、最小值及其圖標(biāo),并將屬性class的值設(shè)置為"list"。
在組件list的內(nèi)部嵌套一個(gè)組件list-item以顯示列表中的每個(gè)列表項(xiàng),并將屬性class的值設(shè)置為"list-item"。通過(guò)動(dòng)態(tài)綁定的方式指定屬性for的值為"{{maxmin}}",從而對(duì)index.js中data里面的maxmin進(jìn)行迭代。
每個(gè)列表項(xiàng)都由一張圖片和一個(gè)文本組成,因此在組件list-item中添加一個(gè)組件image和一個(gè)組件text。
在組件image中將屬性class的值設(shè)置為"icon",并通過(guò)動(dòng)態(tài)綁定的方式將屬性src的值設(shè)置為"/common/{{$item.iconName}}.png"。這樣,report2.js中data里面的maxmin可以是一個(gè)字典的數(shù)組,數(shù)組中的每個(gè)字典都包含一個(gè)key為iconName的元素。
在組件text中將屬性class的值設(shè)置為"maxmin",并通過(guò)動(dòng)態(tài)綁定的方式將顯示的文本設(shè)置為"{{$item.mValue}}"。這樣,對(duì)于index.js中data里面的數(shù)組maxmin,其中的每個(gè)字典都包含一個(gè)key為mValue的元素。
在列表的下方添加一個(gè)組件div以顯示心率在一分鐘內(nèi)的平均次數(shù),并將屬性class的值設(shè)置為" average-container "。
在組件div中嵌套定義三個(gè)組件text,其屬性class的值分別為:"average"、"average-number"和"average",其顯示的文本分別為:平均、{{average}}、次/分。
上述講解請(qǐng)見(jiàn)如下代碼:
- <div class="container" onswipe="toNextPage">
- <div class="title-container">
- <text class="title">
- 心率曲線
- </text>
- </div>
- <div class="chart">
- </div>
- <list class="list">
- <list-item class="list-item" for="{{maxmin}}">
- <image class="icon" src="/common/{{$item.iconName}}.png"/>
- <text class="maxmin">
- {{$item.mValue}}
- </text>
- </list-item>
- </list>
- <div class="average-container">
- <text class="average">
- 平均
- </text>
- <text class="average-number">
- {{average}}
- </text>
- <text class="average">
- 次/分
- </text>
- </div>
- </div>
打開文件index.css。
在類選擇器container中刪除樣式display、left和top。將flex-direction的值設(shè)置為column,以在豎直方向上排列容器內(nèi)的所有組件。將justify-content的值修改為flex-start,以讓容器內(nèi)的所有組件在主軸上向上對(duì)齊。
在類選擇器title中刪除樣式text-align、width和height。將font-size的值修改為38px。將margin-top的值設(shè)置為40px,以讓頁(yè)面標(biāo)題與頁(yè)面的上邊緣保持一定的間距。
添加一個(gè)名為title-container的類選擇器,以設(shè)置頁(yè)面標(biāo)題的樣式。將justify-content和align-items都設(shè)置為center,以讓容器內(nèi)的組件在水平方向和豎直方向都居中對(duì)齊。將寬度width和高度height的值分別設(shè)置為300px和130px。
添加一個(gè)名為chart的類選擇器,以設(shè)置心率曲線圖的樣式。將寬度width和高度height的值分別設(shè)置為400px和180px。
添加一個(gè)名為list的類選擇器,以設(shè)置列表的樣式。將flex-direction的值設(shè)置為row,以在水平方向上排列所有列表項(xiàng)。將寬度width和高度height的值分別設(shè)置為240px和45px。
添加一個(gè)名為list-item的類選擇器,以設(shè)置列表項(xiàng)的樣式。將justify-content和align-items都設(shè)置為center,以讓列表項(xiàng)內(nèi)的組件在水平方向和豎直方向都居中對(duì)齊。將寬度width和高度height的值分別設(shè)置為120px和45px。
添加一個(gè)名為icon的類選擇器,以設(shè)置心率的最大值圖標(biāo)和最小值圖標(biāo)的樣式。將寬度width和高度height的值分別設(shè)置為32px和32px。
添加一個(gè)名為maxmin的類選擇器,以設(shè)置心率的最大值文本和最小值文本的樣式。將font-size的值設(shè)置為24px。將letter-spacing的值設(shè)置為0px,以讓數(shù)字之間的間距更緊湊。
添加一個(gè)名為average-container的類選擇器,以設(shè)置心率平均值的相關(guān)文本的樣式。將justify-content的值設(shè)置為space-between,以讓容器內(nèi)的組件在水平方向上兩端對(duì)齊。將align-items的值設(shè)置為center,以讓容器內(nèi)的組件在豎直方向上居中對(duì)齊。將寬度width和高度height的值分別設(shè)置為280px和55px。
添加一個(gè)名為average-number的類選擇器,以設(shè)置心率平均值的樣式。將font-size的值設(shè)置為38px。將letter-spacing的值設(shè)置為0px,以讓數(shù)字之間的間距更緊湊。
添加一個(gè)名為average的類選擇器,以設(shè)置心率平均值的兩邊文本的樣式。將font-size的值設(shè)置為24px。將color的值設(shè)置為gray,以將文本顯示為灰色。
上述講解請(qǐng)見(jiàn)如下代碼:
- .container {
- display: flex;
- flex-direction: column;
- justify-content: flex-start;
- align-items: center;
- left: 0px;
- top: 0px;
- width: 454px;
- height: 454px;
- }
- .title-container {
- justify-content: center;
- align-items: center;
- width: 300px;
- height: 130px;
- }
- .title {
- margin-top: 40px;
- font-size: 38px;
- text-align: center;
- width: 454px;
- height: 100px;
- }
- .chart {
- width: 400px;
- height: 180px;
- }
- .list {
- flex-direction: row;
- width: 240px;
- height: 45px;
- }
- .list-item {
- justify-content: center;
- align-items: center;
- width: 120px;
- height: 45px;
- }
- .icon {
- width: 32px;
- height: 32px;
- }
- .maxmin {
- font-size: 24px;
- letter-spacing: 0px;
- }
- .average-container {
- justify-content: space-between;
- align-items: center;
- width: 280px;
- height: 55px;
- }
- .average {
- font-size: 24px;
- color: gray;
- }
- .average-number {
- font-size: 38px;
- letter-spacing: 0px;
- }
把心率最大值圖標(biāo)max.png和心率最小值圖標(biāo)min.png添加到目錄common中。
打開文件index.js。
在data中將占位符maxmin初始化為一個(gè)字典數(shù)組。該數(shù)組中包含兩個(gè)字典,分別表示心率最大值和心率最小值的相關(guān)信息。每個(gè)字典中都有兩個(gè)元素,對(duì)應(yīng)的key都是iconName和mValue,分別表示心率最值的圖標(biāo)名稱和心率最值。對(duì)于第一個(gè)字典,將心率最大值的圖標(biāo)名稱iconName初始化為'max',并將心率最大值初始化為0。對(duì)于第二個(gè)字典,將心率最小值的圖標(biāo)名稱iconName初始化為'min',并將心率最小值初始化為0。
在data中將占位符average初始化為0。
上述講解請(qǐng)見(jiàn)如下代碼:
- import router from '@system.router'
- export default {
- data: {
- maxmin: [{
- iconName: 'max',
- mValue: 0
- },
- {
- iconName: 'min',
- mValue: 0
- }],
- average: 0
- }
- }
在index.js中自定義一個(gè)名為getRandomInt的函數(shù),該函數(shù)用于隨機(jī)生成一個(gè)介于min和max之間(包含min和max)的整數(shù)。
在頁(yè)面的生命周期事件函數(shù)onInit()里,首先創(chuàng)建一個(gè)空數(shù)組并賦值給局部作用域變量heartRates,然后通過(guò)for循環(huán)執(zhí)行100次迭代。在每一次迭代中,調(diào)用自定義函數(shù)getRandomInt()隨機(jī)生成一個(gè)介于73和159之間的整數(shù),并調(diào)用函數(shù)push()將隨機(jī)生成的整數(shù)添加到數(shù)組heartRates中。
定義一個(gè)名為countMaxMinAverage的函數(shù),其形參為heartRates,該函數(shù)用于計(jì)算heartRates中所有元素的最大值、最小值和平均值。
在函數(shù)onInit()的最后,調(diào)用自定義函數(shù)countMaxMinAverage (),并將heartRates作為實(shí)參傳遞給形參heartRates。
上述講解請(qǐng)見(jiàn)如下代碼:
- import router from '@system.router'
- export default {
- data: {
- ......
- },
- onInit() {
- let heartRates = [];
- for (let i = 0; i < 100; i++) {
- heartRates.push(this.getRandomInt(73, 159));
- }
- this.countMaxMinAverage(heartRates);
- },
- getRandomInt(min, max) {
- return Math.floor(Math.random() * (max - min + 1) ) + min;
- },
- countMaxMinAverage(heartRates) {
- }
- }
在自定義函數(shù)countMaxMinAverage ()的函數(shù)體中,分別調(diào)用Math.max.apply()和Math.min.apply()計(jì)算數(shù)組heartRates中的最大值和最小值,然后分別賦值給data中的maxmin[0].mValue和maxmin[1].mValue。通過(guò)for循環(huán)對(duì)數(shù)組heartRates中的所有心率數(shù)據(jù)進(jìn)行遍歷,在遍歷的過(guò)程中將心率數(shù)據(jù)累加到局部作用域變量sum,以計(jì)算數(shù)組heartRates中所有心率數(shù)據(jù)的總和。求出總和之后,將其除以所有心率數(shù)據(jù)的個(gè)數(shù)就得到了所有心率數(shù)據(jù)的平均值。調(diào)用函數(shù)Math.round()返回與心率平均值最接近的整數(shù),并將其賦值給data中的average。
上述講解請(qǐng)見(jiàn)如下代碼:
- import router from '@system.router'
- export default {
- ......
- countMaxMinAverage(heartRates) {
- this.maxmin[0].mValue = Math.max.apply(null, heartRates);
- this.maxmin[1].mValue = Math.min.apply(null, heartRates);
- let sum = 0;
- for (let index = 0; index < heartRates.length; index++) {
- sum += heartRates[index];
- }
- this.average = Math.round(sum / heartRates.length);
- },
- ......
- }
保存所有代碼后打開模擬器,在頁(yè)面中顯示出了頁(yè)面標(biāo)題、心率最大值及其圖標(biāo)、心率最小值及其圖標(biāo)、心率在每分鐘內(nèi)的平均次數(shù),運(yùn)行效果如圖所示:

打開文件index.hml。
將組件list上方的組件div修改為chart,以繪制一張心率曲線圖。在組件chart中,通過(guò)動(dòng)態(tài)綁定的方式將屬性options和datasets的值分別設(shè)置為"{{options}}"和"{{datasets}}"。
上述講解請(qǐng)見(jiàn)如下代碼:
- <div class="container" onswipe="toNextPage">
- <div class="title-container">
- <text class="title">
- 心率曲線
- </text>
- </div>
- <chart class="chart" options="{{options}}" datasets="{{datasets}}"/>
- <list class="list">
- ......
- </list>
- ......
- </div
打開文件index.js。
在data中將占位符options的值初始化為一個(gè)字典,字典中包含兩個(gè)元素,分別用于設(shè)置x軸和y軸的參數(shù)。第一個(gè)元素的key是xAxis,對(duì)應(yīng)的value是一個(gè)空字典{},說(shuō)明不需要對(duì)x軸的參數(shù)進(jìn)行設(shè)置。第二個(gè)元素的key是yAxis,對(duì)應(yīng)的value是一個(gè)由兩個(gè)元素組成的字典,分別用于設(shè)置y軸的最小值和最大值,其中,key分別是min和max,value分別是0和160。
在data中將占位符datasets的值初始化為一個(gè)字典的數(shù)組,該數(shù)組中只包含一個(gè)字典,該字典中包含兩個(gè)元素。第一個(gè)元素的key是gradient,對(duì)應(yīng)的value是true,用于表示折線向下填充顏色到x軸。第二個(gè)元素的key是data,對(duì)應(yīng)的value是一個(gè)空數(shù)組[],用于指定心率圖中的數(shù)據(jù)。
在頁(yè)面的生命周期事件函數(shù)onInit()中,在隨機(jī)生成100個(gè)整數(shù)之后將所有整數(shù)組成的數(shù)組賦值給data中的datasets[0].data。
上述講解請(qǐng)見(jiàn)如下代碼:
- import router from '@system.router'
- export default {
- data: {
- ......
- average: 0,
- options: {
- xAxis: {},
- yAxis: {
- min: 0,
- max: 160
- }
- },
- datasets: [{
- gradient: true,
- data: []
- }]
- },
- onInit() {
- let heartRates = [];
- for (let i = 0; i < 100; i++) {
- heartRates.push(this.getRandomInt(73, 159));
- }
- this.datasets[0].data = heartRates;
- this.countMaxMinAverage(heartRates);
- },
- ......
- }
保存所有代碼后打開模擬器,運(yùn)行效果如下圖所示:

項(xiàng)目源代碼,請(qǐng)見(jiàn)附件。
歡迎訂閱我的專欄【圖解鴻蒙】:
https://harmonyos.51cto.com/column/27
©著作權(quán)歸作者和HarmonyOS技術(shù)社區(qū)共同所有,如需轉(zhuǎn)載,請(qǐng)注明出處,否則將追究法律責(zé)任
想了解更多內(nèi)容,請(qǐng)?jiān)L問(wèn):
51CTO和華為官方合作共建的鴻蒙技術(shù)社區(qū)
https://harmonyos.51cto.com/#zz