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 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 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 370