• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import CommonData from '../common/CommonData';
17import Constants from '../constant/Constants';
18import { OffscreenCanvas } from '../model/OffscreenCanvas'; // 离屏画布类
19import { TimeUtils } from '../utils/TimeUtils'; // 时间计算工具类
20
21/**
22 * 年视图子组件
23 */
24@Component
25export struct YearViewItem {
26  // 年视图离屏画布列表
27  @State yearViewList: Array<OffscreenCanvas> = [];
28  @Prop @Watch('updateYearData') year: number;
29  // 年视图月份点击回调
30  onMonthClick: (year: number, month: number) => void = () => {
31  };
32
33  /**
34   * 更新年数据
35   */
36  updateYearData() {
37    this.yearViewList = [];
38    for (let i = 1; i <= Constants.MONTHS_NUM; i++) {
39      this.yearViewList.push(new OffscreenCanvas(this.year, i));
40    }
41  }
42
43  aboutToAppear(): void {
44    // 添加年视图中每个月的离屏画布对象。一个画布对象绘制一个月数据。
45    for (let i = 1; i <= Constants.MONTHS_NUM; i++) {
46      this.yearViewList.push(new OffscreenCanvas(this.year, i));
47    }
48  }
49
50  build() {
51    Grid() {
52      ForEach(this.yearViewList, (monthItem: OffscreenCanvas) => {
53        GridItem() {
54          // TODO: 高性能知识点: 年视图使用Canvas绘制显示年视图中每个月,以减少节点数量,同时使用OffscreenCanvasRenderingContext2D离
55          // 屏绘制,将需要绘制的内容先绘制在缓存区,然后将其转换成图片,一次性绘制到canvas上,以加快绘制速度。
56          Canvas(monthItem.context)
57            .width($r('app.string.calendar_switch_full_size'))
58            .height($r('app.string.calendar_switch_full_size'))
59            .onReady(() => {
60              // 绘制年视图中一个月的数据
61              let isTodayMoth: boolean =
62                monthItem.year === Constants.TODAY_YEAR && monthItem.month === Constants.TODAY_MONTH;
63              // 画月
64              monthItem.offContext.font = Constants.YEAR_VIEW_MONTH_FONT[CommonData.DEVICE_TYPE];
65              monthItem.offContext.fillStyle = isTodayMoth ? Color.Red : Color.Black;
66              monthItem.offContext.fillText(Constants.MONTHS[monthItem.month-1],
67                Constants.YEAR_VIEW_INIT_THREE[CommonData.DEVICE_TYPE],
68                Constants.YEAR_VIEW_MONTH_HEIGHT[CommonData.DEVICE_TYPE]);
69              // 水平偏移
70              let horizontalOffset = Constants.YEAR_VIEW_INIT_THREE[CommonData.DEVICE_TYPE];
71              // 画星期
72              monthItem.offContext.font = Constants.YEAR_VIEW_WEEK_FONT[CommonData.DEVICE_TYPE];
73              for (let i = 0; i < Constants.WEEKS.length; i++) {
74                // 星期六,日字体颜色设置灰色
75                if (i === 0 || i === 6) {
76                  monthItem.offContext.fillStyle = Constants.YEAR_VIEW_FONT_COLOR;
77                }
78                monthItem.offContext.fillText(Constants.WEEKS[i], horizontalOffset,
79                  Constants.YEAR_VIEW_WEEK_HEIGHT[CommonData.DEVICE_TYPE]);
80                monthItem.offContext.fillStyle = Color.Black;
81                horizontalOffset += Constants.YEAR_VIEW_HORIZONTAL_OFFSET[CommonData.DEVICE_TYPE];
82              }
83              // 获取月份日期前占位个数
84              const INTERVAL_COUNT: number = TimeUtils.getWeekDay(monthItem.year, monthItem.month, 1);
85              // 画日期
86              monthItem.offContext.font = Constants.YEAR_VIEW_DAY_FONT[CommonData.DEVICE_TYPE];
87              monthItem.offContext.fillStyle = Color.Black;
88              // 获取每个月的总天数
89              const TOTAL_DAYS_IN_MONTH: number = TimeUtils.getMonthDays(monthItem.year, monthItem.month);
90              // 获取一个月占几周。向上取整
91              const WEEK_LENGTH = Math.ceil((INTERVAL_COUNT + TOTAL_DAYS_IN_MONTH) / 7);
92              // 初始化绘制日期。从1号开始绘制
93              let dayIndex = 1;
94              // 日期垂直偏移
95              let verticalOffset = Constants.YEAR_VIEW_DAY_HEIGHT[CommonData.DEVICE_TYPE];
96              for (let i = 0; i < WEEK_LENGTH; i++) {
97                horizontalOffset = Constants.YEAR_VIEW_INIT_THREE[CommonData.DEVICE_TYPE];
98                // 画一周
99                for (let j = 1; j <= Constants.DAYS_IN_WEEK; j++) {
100                  if (i === 0 && j <= INTERVAL_COUNT) {
101                    // 月份日期前占位
102                  } else {
103                    // 判断绘制的日期是不是今天。如果是今天,日期绘制圆圈红色背景,白色字体。如果不是今天,黑色字体
104                    if (isTodayMoth && Constants.TODAY === dayIndex) {
105
106                      // 画圆圈
107                      monthItem.offContext.fillStyle = Color.Red;
108                      // 绘制弧线路径。这里绘制圆圈。arc入参分别是弧线圆心的x坐标值,弧线圆心的y坐标值,弧线的圆半径,弧线的起始弧度,弧线的
109                      // 终止弧度。5和3是圆圈x,y坐标绘制位置的微调值
110                      monthItem.offContext.arc(horizontalOffset + 5 +
111                      Constants.YEAR_VIEW_X_AXIS[CommonData.DEVICE_TYPE],
112                        verticalOffset - 3 - Constants.YEAR_VIEW_Y_AXIS[CommonData.DEVICE_TYPE],
113                        Constants.YEAR_VIEW_TODAY_RADIUS[CommonData.DEVICE_TYPE], Constants.DEFAULT,
114                        Constants.YEAR_VIEW_TODAY_END_ANGLE[CommonData.DEVICE_TYPE]);
115                      // 对封闭路径进行填充。
116                      monthItem.offContext.fill();
117                      // 设置白色字体
118                      monthItem.offContext.fillStyle = Color.White;
119                    } else {
120                      if (j === 1 || j === 7) {
121                        // 星期日和星期六字体设置灰色
122                        monthItem.offContext.fillStyle = Constants.YEAR_VIEW_FONT_COLOR;
123                      } else {
124                        monthItem.offContext.fillStyle = Color.Black;
125                      }
126                    }
127                    // 画日期
128                    if (dayIndex < 10) {
129                      // 日期1-9号,字体水平位置微调3vp
130                      monthItem.offContext.fillText(dayIndex.toString(), 3 + horizontalOffset, verticalOffset);
131                    } else {
132                      monthItem.offContext.fillText(dayIndex.toString(), horizontalOffset, verticalOffset);
133                    }
134                    // 画线。如果是农历初一,则日期底部加下划线
135                    if (TimeUtils.isLunarFirstDayOfMonth(monthItem.year, monthItem.month, dayIndex)) {
136                      monthItem.offContext.font = Constants.YEAR_VIEW_UNDERLINE[CommonData.DEVICE_TYPE];
137                      monthItem.offContext.fillStyle = Color.Red;
138                      monthItem.offContext.fillText('_', 1 + horizontalOffset, verticalOffset);
139                      monthItem.offContext.font = Constants.YEAR_VIEW_DAY_FONT[CommonData.DEVICE_TYPE];
140                    }
141                    // 重置日期字体颜色
142                    monthItem.offContext.fillStyle = Color.Black;
143                    dayIndex++;
144                  }
145                  // 日期绘制水平偏移
146                  horizontalOffset += Constants.YEAR_VIEW_HORIZONTAL_OFFSET[CommonData.DEVICE_TYPE];
147                  if (dayIndex > TOTAL_DAYS_IN_MONTH) {
148                    break;
149                  }
150                }
151                // 周绘制垂直偏移
152                verticalOffset += Constants.YEAR_VIEW_VERTICAL_OFFSET[CommonData.DEVICE_TYPE];
153              }
154              // 从OffscreenCanvas组件中最近渲染的图像创建一个ImageBitmap对象
155              const IMAGE = monthItem.offContext.transferToImageBitmap();
156              // 显示给定的ImageBitmap对象
157              monthItem.context.transferFromImageBitmap(IMAGE);
158            })
159        }
160        .height($r('app.string.calendar_switch_size_twenty_three'))
161        .width($r('app.string.calendar_switch_size_twenty_five'))
162        .onClick(() => {
163          this.onMonthClick(monthItem.year, monthItem.month);
164        })
165      }, (monthItem: OffscreenCanvas) => monthItem.year + '' + monthItem.month + '' + CommonData.DEVICE_TYPE)
166    }
167    .onSizeChange(() => {
168      if (CommonData.IS_FOLD) {
169        // 折叠屏展开态和折叠态尺寸变化时需要刷新当前年视图
170        this.updateYearData();
171      }
172    })
173    .scrollBar(BarState.Off)
174    .columnsTemplate('1fr 1fr 1fr')
175    .columnsGap($r('app.integer.calendar_switch_columns_gap'))
176    .rowsGap($r('app.integer.calendar_switch_rows_gap'))
177  }
178}