• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Drawing Modifier
2
3If the drawn content of some components does not meet the requirements, you can use the custom drawing features to draw part or all of the components to achieve the expected effect. For example, you can create buttons in special shapes or icons that mix text and imagery. The drawing modifier offers higher flexibility in your custom drawing.
4
5> **NOTE**
6>
7> The initial APIs of this module are supported since API version 12. Updates will be marked with a superscript to indicate their earliest API version.
8
9## drawModifier
10
11drawModifier(modifier: DrawModifier | undefined): T
12
13Creates a drawing modifier.
14
15**Atomic service API**: This API can be used in atomic services since API version 12.
16
17**System capability**: SystemCapability.ArkUI.ArkUI.Full
18
19**Supported components:**
20
21AlphabetIndexer, Badge, Blank, Button, CalendarPicker, Checkbox, CheckboxGroup, Circle, Column, ColumnSplit, Counter, DataPanel, DatePicker, Ellipse, Flex, FlowItem, FolderStack, FormLink, Gauge, Grid, GridCol, GridItem, GridRow, Hyperlink, Image, ImageAnimator, ImageSpan, Line, List, ListItem, ListItemGroup, LoadingProgress, Marquee, Menu, MenuItem, MenuItemGroup, NavDestination, Navigation, Navigator, NavRouter, NodeContainer, Path, PatternLock, Polygon, Polyline, Progress, QRCode, Radio, Rating, Rect, Refresh, RelativeContainer, RichEditor, Row, RowSplit, Scroll, ScrollBar, Search, Select, Shape, SideBarContainer, Slider, Stack, Stepper, StepperItem, Swiper, SymbolGlyph, TabContent, Tabs, Text, TextArea, TextClock, TextInput, TextPicker, TextTimer, TimePicker, Toggle, WaterFlow, XComponent
22
23**Parameters**
24
25| Name| Type                                                | Mandatory| Description                                                        |
26| ------ | ---------------------------------------------------- | ---- | ------------------------------------------------------------ |
27| modifier  |  [DrawModifier](#drawmodifier-1) \| undefined | Yes  | Custom drawing modifier, which defines the logic of custom drawing.<br> Default value: **undefined**<br>**NOTE**<br> A custom modifier applies only to the FrameNode of the currently bound component, not to its subnodes.|
28
29**Return value**
30
31| Type| Description|
32| --- | --- |
33| T | Current component.|
34
35## DrawModifier
36
37Implements a **DrawModifier** instance for using the **drawForeground**, **drawFront**, **drawContent**, and **drawBehind** APIs for custom drawing as well as the [invalidate](#invalidate) API for redrawing. Each **DrawModifier** instance can be set for only one component. Repeated setting is not allowed.
38
39The figure below shows the custom drawing layers.
40
41![drawModifier.gif](figures/drawModifier.png)
42
43**Atomic service API**: This API can be used in atomic services since API version 12.
44
45**System capability**: SystemCapability.ArkUI.ArkUI.Full
46
47### drawFront
48
49drawFront?(drawContext: DrawContext): void
50
51Draws the content foreground. Override this method to implement custom content foreground drawing.
52
53**Atomic service API**: This API can be used in atomic services since API version 12.
54
55**System capability**: SystemCapability.ArkUI.ArkUI.Full
56
57**Parameters**
58
59| Name | Type                                                  | Mandatory| Description            |
60| ------- | ------------------------------------------------------ | ---- | ---------------- |
61| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | Yes  | Graphics drawing context.|
62
63### drawContent
64
65drawContent?(drawContext: DrawContext): void
66
67Draws the content. Override this method to implement custom content drawing, which will replace the component's default content drawing function.
68
69**Atomic service API**: This API can be used in atomic services since API version 12.
70
71**System capability**: SystemCapability.ArkUI.ArkUI.Full
72
73**Parameters**
74
75| Name | Type                                                  | Mandatory| Description            |
76| ------- | ------------------------------------------------------ | ---- | ---------------- |
77| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | Yes  | Graphics drawing context.|
78
79### drawBehind
80
81drawBehind?(drawContext: DrawContext): void
82
83Draws the background. Override this method to implement custom background drawing.
84
85**Atomic service API**: This API can be used in atomic services since API version 12.
86
87**System capability**: SystemCapability.ArkUI.ArkUI.Full
88
89**Parameters**
90
91| Name | Type                                                  | Mandatory| Description            |
92| ------- | ------------------------------------------------------ | ---- | ---------------- |
93| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | Yes  | Graphics drawing context.|
94
95### drawForeground<sup>20+</sup>
96
97drawForeground?(drawContext: DrawContext): void
98
99Draws the foreground. Override this method to implement custom foreground drawing.
100
101**Atomic service API**: This API can be used in atomic services since API version 20.
102
103**System capability**: SystemCapability.ArkUI.ArkUI.Full
104
105**Parameters**
106
107| Name | Type                                                  | Mandatory| Description            |
108| ------- | ------------------------------------------------------ | ---- | ---------------- |
109| drawContext | [DrawContext](../js-apis-arkui-graphics.md#drawcontext) | Yes  | Graphics drawing context.|
110
111### invalidate
112
113invalidate(): void
114
115Triggers redrawing of the bound component. No overloading is allowed or needed.
116
117**Atomic service API**: This API can be used in atomic services since API version 12.
118
119**System capability**: SystemCapability.ArkUI.ArkUI.Full
120
121## Example
122
123### Example 1: Implementing Custom Drawing Through DrawModifier
124
125This example shows how to implement custom drawing for a **Text** component using **DrawModifier**.
126
127```ts
128// xxx.ets
129import { drawing } from '@kit.ArkGraphics2D';
130import { AnimatorResult } from '@kit.ArkUI';
131
132class MyFullDrawModifier extends DrawModifier {
133  public scaleX: number = 1;
134  public scaleY: number = 1;
135  uiContext: UIContext;
136
137  constructor(uiContext: UIContext) {
138    super();
139    this.uiContext = uiContext;
140  }
141
142  drawBehind(context: DrawContext): void {
143    const brush = new drawing.Brush();
144    brush.setColor({
145      alpha: 255,
146      red: 255,
147      green: 0,
148      blue: 0
149    });
150    context.canvas.attachBrush(brush);
151    const halfWidth = context.size.width / 2;
152    const halfHeight = context.size.width / 2;
153    context.canvas.drawRect({
154      left: this.uiContext.vp2px(halfWidth - 50 * this.scaleX),
155      top: this.uiContext.vp2px(halfHeight - 50 * this.scaleY),
156      right: this.uiContext.vp2px(halfWidth + 50 * this.scaleX),
157      bottom: this.uiContext.vp2px(halfHeight + 50 * this.scaleY)
158    });
159  }
160
161  drawContent(context: DrawContext): void {
162    const brush = new drawing.Brush();
163    brush.setColor({
164      alpha: 255,
165      red: 0,
166      green: 255,
167      blue: 0
168    });
169    context.canvas.attachBrush(brush);
170    const halfWidth = context.size.width / 2;
171    const halfHeight = context.size.width / 2;
172    context.canvas.drawRect({
173      left: this.uiContext.vp2px(halfWidth - 30 * this.scaleX),
174      top: this.uiContext.vp2px(halfHeight - 30 * this.scaleY),
175      right: this.uiContext.vp2px(halfWidth + 30 * this.scaleX),
176      bottom: this.uiContext.vp2px(halfHeight + 30 * this.scaleY)
177    });
178  }
179
180  drawFront(context: DrawContext): void {
181    const brush = new drawing.Brush();
182    brush.setColor({
183      alpha: 255,
184      red: 0,
185      green: 0,
186      blue: 255
187    });
188    context.canvas.attachBrush(brush);
189    const halfWidth = context.size.width / 2;
190    const halfHeight = context.size.width / 2;
191    const radiusScale = (this.scaleX + this.scaleY) / 2;
192    context.canvas.drawCircle(this.uiContext.vp2px(halfWidth), this.uiContext.vp2px(halfHeight), this.uiContext.vp2px(20 * radiusScale));
193  }
194}
195
196class MyFrontDrawModifier extends DrawModifier {
197  public scaleX: number = 1;
198  public scaleY: number = 1;
199  uiContext: UIContext;
200
201  constructor(uiContext: UIContext) {
202    super();
203    this.uiContext = uiContext;
204  }
205
206  drawFront(context: DrawContext): void {
207    const brush = new drawing.Brush();
208    brush.setColor({
209      alpha: 255,
210      red: 0,
211      green: 0,
212      blue: 255
213    });
214    context.canvas.attachBrush(brush);
215    const halfWidth = context.size.width / 2;
216    const halfHeight = context.size.width / 2;
217    const radiusScale = (this.scaleX + this.scaleY) / 2;
218    context.canvas.drawCircle(this.uiContext.vp2px(halfWidth), this.uiContext.vp2px(halfHeight), this.uiContext.vp2px(20 * radiusScale));
219  }
220}
221
222@Entry
223@Component
224struct DrawModifierExample {
225  private fullModifier: MyFullDrawModifier = new MyFullDrawModifier(this.getUIContext());
226  private frontModifier: MyFrontDrawModifier = new MyFrontDrawModifier(this.getUIContext());
227  private drawAnimator: AnimatorResult | undefined = undefined;
228  @State modifier: DrawModifier = new MyFrontDrawModifier(this.getUIContext());
229  private count = 0;
230
231  create() {
232    let self = this;
233    this.drawAnimator = this.getUIContext().createAnimator({
234      duration: 1000,
235      easing: 'ease',
236      delay: 0,
237      fill: 'forwards',
238      direction: 'normal',
239      iterations: 1,
240      begin: 0,
241      end: 2
242    });
243    this.drawAnimator.onFrame = (value: number) => {
244      console.log('frame value =', value);
245      const tempModifier = self.modifier as MyFullDrawModifier | MyFrontDrawModifier;
246      tempModifier.scaleX = Math.abs(value - 1);
247      tempModifier.scaleY = Math.abs(value - 1);
248      self.modifier.invalidate();
249    };
250  }
251
252  build() {
253    Column() {
254      Row() {
255        Text('test text')
256          .width(100)
257          .height(100)
258          .margin(10)
259          .backgroundColor(Color.Gray)
260          .onClick(() => {
261            const tempModifier = this.modifier as MyFullDrawModifier | MyFrontDrawModifier;
262            tempModifier.scaleX -= 0.1;
263            tempModifier.scaleY -= 0.1;
264          })
265          .drawModifier(this.modifier)
266      }
267
268      Row() {
269        Button('create')
270          .width(100)
271          .height(100)
272          .margin(10)
273          .onClick(() => {
274            this.create();
275          })
276        Button('play')
277          .width(100)
278          .height(100)
279          .margin(10)
280          .onClick(() => {
281            if (this.drawAnimator) {
282              this.drawAnimator.play();
283            }
284          })
285        Button('changeModifier')
286          .width(100)
287          .height(100)
288          .margin(10)
289          .onClick(() => {
290            this.count += 1;
291            if (this.count % 2 === 1) {
292              console.log('change to full modifier');
293              this.modifier = this.fullModifier;
294            } else {
295              console.log('change to front modifier');
296              this.modifier = this.frontModifier;
297            }
298          })
299      }
300    }
301    .width('100%')
302    .height('100%')
303  }
304}
305```
306
307![drawModifier.gif](figures/drawModifier.gif)
308
309### Example 2: Implementing Custom Foreground Drawing for a Container Through DrawModifier
310
311This example demonstrates how to implement custom foreground drawing for a **Column** container using **DrawModifier**.
312
313```ts
314// xxx.ets
315import { drawing } from '@kit.ArkGraphics2D';
316
317class MyForegroundDrawModifier extends DrawModifier {
318  public scaleX: number = 3;
319  public scaleY: number = 3;
320  uiContext: UIContext;
321
322  constructor(uiContext: UIContext) {
323    super();
324    this.uiContext = uiContext;
325  }
326
327  drawForeground(context: DrawContext): void {
328    const brush = new drawing.Brush();
329    brush.setColor({
330      alpha: 255,
331      red: 0,
332      green: 50,
333      blue: 100
334    });
335    context.canvas.attachBrush(brush);
336    const halfWidth = context.size.width / 2;
337    const halfHeight = context.size.width / 2;
338    context.canvas.drawRect({
339      left: this.uiContext.vp2px(halfWidth - 30 * this.scaleX),
340      top: this.uiContext.vp2px(halfHeight - 30 * this.scaleY),
341      right: this.uiContext.vp2px(halfWidth + 30 * this.scaleX),
342      bottom: this.uiContext.vp2px(halfHeight + 30 * this.scaleY)
343    });
344  }
345}
346
347@Entry
348@Component
349struct DrawModifierExample {
350  private foregroundModifier: MyForegroundDrawModifier = new MyForegroundDrawModifier(this.getUIContext());
351
352  build() {
353    Column() {
354      Text('Here is a child node')
355        .fontSize(36)
356        .width('100%')
357        .height('100%')
358        .textAlign(TextAlign.Center)
359    }
360    .margin(50)
361    .width(280)
362    .height(300)
363    .backgroundColor(0x87CEEB)
364    .drawModifier(this.foregroundModifier)
365  }
366}
367
368```
369![drawForeground.png](figures/drawForeground.png)
370