1# Basic Camera Animation (ArkTS) 2 3When using the camera, transitions such as changing camera modes or switching between front and rear cameras will always involve replacing the preview stream. To enhance user experience, smooth animations can be effectively incorporated. This topic describes how to use preview stream snapshots and ArkUI's [explicit animations](../../reference/apis-arkui/arkui-ts/ts-explicit-animatetoimmediately.md) to implement three key scene transitions: 4 5- Mode switching: Use preview stream snapshots to create a blur effect for transition. 6 7 The following depicts the transition from video mode to photo mode. 8 9  10 11- Front/Rear camera switching: Use preview stream snapshots to create a blur-and-flip effect for transition. 12 13 The following demonstrates the transition from using the front camera to the rear camera. 14 15  16 17- Photo capture blackout: Use a blackout component to overlay the preview stream to create a blackout transition. 18 19 The following depicts the moment of photo capture. 20 21  22 23## Blackout Animation 24 25Use component overlay to implement the blackout effect. 26 271. Import dependencies. Specifically, import the camera, image, and ArkUI modules. 28 29 ```ts 30 import { curves } from '@kit.ArkUI'; 31 ``` 32 332. Build a blackout component. 34 35 Define a blackout component, which is displayed during camera blackouts for photo capture or front/rear camera switching. This component will block the XComponent. 36 37 Define the component's properties as follows: 38 39 ```ts 40 @State isShowBlack: boolean = false; // Whether to show the blackout component. 41 @StorageLink('captureClick') @Watch('onCaptureClick') captureClickFlag: number = 0; // Entry for the blackout animation. 42 @State flashBlackOpacity: number = 1; // Opacity of the blackout component. 43 ``` 44 45 Implement the logic of the blackout component as follows: 46 47 ```ts 48 // The component is displayed during camera blackouts for photo capture or camera switching and is used to block the XComponent. 49 if (this.isShowBlack) { 50 Column() 51 .key('black') 52 .width(px2vp(1080)) // The width and height must be the same as those of the XComponent of the preview stream. The layer is above the preview stream and below the snapshot component. 53 .height(px2vp(1920)) 54 .backgroundColor(Color.Black) 55 .opacity(this.flashBlackOpacity) 56 } 57 ``` 58 593. Implement the blackout effect. 60 61 ```ts 62 function flashBlackAnim() { 63 console.info('flashBlackAnim E'); 64 this.flashBlackOpacity = 1; // The blackout component is opaque. 65 this.isShowBlack = true; // Show the blackout component. 66 animateToImmediately({ 67 curve: curves.interpolatingSpring(1, 1, 410, 38), 68 delay: 50, // A black screen is displayed after a delay of 50 ms. 69 onFinish: () => { 70 this.isShowBlack = false; // Hide the blackout component. 71 this.flashBlackOpacity = 1; 72 console.info('flashBlackAnim X'); 73 } 74 }, () => { 75 this.flashBlackOpacity = 0; // The blackout component is changed from opaque to transparent. 76 }) 77 } 78 ``` 79 804. Trigger the blackout effect. 81 82 When a user touches the **PHOTO** button, the value of **CaptureClick** bound to the StorageLink is updated, and **onCaptureClick** is invoked. In this case, the animation starts to play. 83 84 ```ts 85 onCaptureClick(): void { 86 console.info('onCaptureClick'); 87 console.info('onCaptureClick'); 88 this.flashBlackAnim(); 89 } 90 ``` 91 92 93 94## Blur Animation 95 96You can use preview stream snapshots to create a blur effect for mode switching or front/rear camera switching. 97 981. Import dependencies. Specifically, import the camera, image, and ArkUI modules. 99 100 ```ts 101 import { camera } from '@kit.CameraKit'; 102 import { image } from '@kit.ImageKit'; 103 import { curves } from '@kit.ArkUI'; 104 ``` 105 1062. Obtain a preview stream snapshot. 107 108 Preview stream snapshots are obtained by calling [image.createPixelMapFromSurface](../../reference/apis-image-kit/js-apis-image.md#imagecreatepixelmapfromsurface11) provided by the image module. In this API, **surfaceId** is the surface ID of the current preview stream, and **size** is the width and height of the current preview stream profile. Create a snapshot utility class (TS file), import the dependency, and export the snapshot retrieval API for the page to use. The code snippet below shows the implementation of the snapshot utility class: 109 110 ```ts 111 import { image } from '@kit.ImageKit'; 112 113 export class BlurAnimateUtil { 114 public static surfaceShot: image.PixelMap; 115 116 /** 117 * Obtain a surface snapshot. 118 * @param surfaceId 119 * @returns 120 */ 121 public static async doSurfaceShot(surfaceId: string) { 122 console.info(`doSurfaceShot surfaceId:${surfaceId}.`); 123 if (surfaceId === '') { 124 console.error('surface not ready!'); 125 return; 126 } 127 try { 128 if (this.surfaceShot) { 129 await this.surfaceShot.release(); 130 } 131 this.surfaceShot = await image.createPixelMapFromSurface(surfaceId, { 132 size: { width: 1920, height: 1080 }, // Obtain the width and height of the preview stream profile. 133 x: 0, 134 y: 0 135 }); 136 let imageInfo: image.ImageInfo = await this.surfaceShot.getImageInfo(); 137 console.info('doSurfaceShot surfaceShot:' + JSON.stringify(imageInfo.size)); 138 } catch (err) { 139 console.error(JSON.stringify(err)); 140 } 141 } 142 143 /** 144 * Obtain the snapshot captured by calling doSurfaceShot. 145 * @returns 146 */ 147 public static getSurfaceShot(): image.PixelMap { 148 return this.surfaceShot; 149 } 150 } 151 ``` 152 1533. Build a snapshot component. 154 155 Define a snapshot component, and place it above the XComponent of the preview stream to block the XComponent. 156 157 Define the component's properties as follows: 158 159 ```ts 160 @State isShowBlur: boolean = false; // Whether to show the snapshot component. 161 @StorageLink('modeChange') @Watch('onModeChange') modeChangeFlag: number = 0; // Entry for triggering the mode switching animation. 162 @StorageLink('switchCamera') @Watch('onSwitchCamera') switchCameraFlag: number = 0; // Entry for triggering the front/rear camera switching animation. 163 @StorageLink('frameStart') @Watch('onFrameStart') frameStartFlag: number = 0; // Entry for the fade-out animation. 164 @State screenshotPixelMap: image.PixelMap | undefined = undefined; // PixelMap of the snapshot component. 165 @State surfaceId: string ="; // Surface ID of the XComponent of the current preview stream. 166 @StorageLink('curPosition') curPosition: number = 0; // Current camera position (front or rear). 167 @State shotImgBlur: number = 0; // Blur degree of the snapshot component. 168 @State shotImgOpacity: number = 1; // Opacity of the snapshot component. 169 @State shotImgScale: ScaleOptions = {x: 1, y: 1}; // Scale ratio of the snapshot component. 170 @State shotImgRotation: RotateOptions = { y: 0.5, angle: 0 } // Rotation angle of the snapshot component. 171 ``` 172 173 Implement the snapshot component as follows: 174 175 ```ts 176 // Snapshot component is placed above the XComponent of the preview stream. 177 if (this.isShowBlur) { 178 Column() { 179 Image(this.screenshotPixelMap) 180 .blur(this.shotImgBlur) 181 .opacity(this.shotImgOpacity) 182 .rotate(this.shotImgRotation) // Provided by ArkUI for component rotation. 183 .scale(this.shotImgScale) 184 .width(px2vp(1080)) // The width and height must be the same as those of the XComponent of the preview stream. The layer is above the preview stream. 185 .height(px2vp(1920)) 186 .syncLoad(true) 187 } 188 .width(px2vp(1080)) 189 .height(px2vp(1920)) 190 } 191 ``` 192 1934. (Optional) Implement the fade-in blur effect. 194 195 The mode switching animation is implemented in two phases: fade-in blur animation and fade-out blur animation. 196 197 When a user touches a button, and the camera takes a snapshot of the preview stream. The snapshot component is displayed, which gradually blurs over the existing preview stream, implementing the fade-in blur animation. 198 199 > **NOTE** 200 > 201 > The **image.createPixelMapFromSurface** API, which is used to extract the surface content into a PixelMap, operates differently from the rendering logic of the XComponent. As such, different rotation compensations must be applied to both the image content and the component for the front and rear cameras. 202 203 ```ts 204 async function showBlurAnim() { 205 console.info('showBlurAnim E'); 206 // Obtain the surface snapshot. 207 let shotPixel = BlurAnimateUtil.getSurfaceShot(); 208 // The rear camera is used. 209 if (this.curPosition === 0) { 210 console.info('showBlurAnim BACK'); 211 // For candy bar phones, a 90° rotation compensation is applied to the content for a snapshot taken with the rear camera. 212 await shotPixel.rotate(90); // Provided by Image Kit for image content rotation. 213 // For candy bar phones, a 0° rotation compensation is applied to the component for a snapshot taken with the rear camera. 214 this.shotImgRotation = { y: 0.5, angle: 0 }; 215 } else { 216 console.info('showBlurAnim FRONT'); 217 // For candy bar phones, a 270° rotation compensation is applied to the content for a snapshot taken with the front camera. 218 await shotPixel.rotate(270); 219 // For candy bar phones, a 180° rotation compensation is applied to the component for a snapshot taken with the front camera. 220 this.shotImgRotation = { y: 0.5, angle: 180 }; 221 } 222 this.screenshotPixelMap = shotPixel; 223 // Initialize animation parameters. 224 this.shotImgBlur = 0; // No blur. 225 this.shotImgOpacity = 1; // Opaque. 226 this.isShowBlur = true; // Show the snapshot component. 227 animateToImmediately( 228 { 229 duration: 200, 230 curve: Curve.Friction, 231 onFinish: async () => { 232 console.info('showBlurAnim X'); 233 } 234 }, 235 () => { 236 this.shotImgBlur = 48; // Blur intensity of the snapshot component. 237 } 238 ); 239 } 240 ``` 241 2425. Implement the fade-out blur animation. 243 244 The fade-out blur animation is triggered by the event [on('frameStart')](../../reference/apis-camera-kit/js-apis-camera.md#onframestart) of the new preview stream. During this effect, the snapshot component gradually becomes clear, revealing the new preview stream. 245 246 ```ts 247 function hideBlurAnim(): void { 248 this.isShowBlack = false; 249 console.info('hideBlurAnim E'); 250 animateToImmediately({ 251 duration: 200, 252 curve: Curve.FastOutSlowIn, 253 onFinish: () => { 254 this.isShowBlur = false; // Hide the blur component. 255 this.shotImgBlur = 0; 256 this.shotImgOpacity = 1; 257 console.info('hideBlurAnim X'); 258 } 259 }, () => { 260 // Change the opacity of the snapshot component. 261 this.shotImgOpacity = 0; // Opacity change of the snapshot component. 262 }); 263 } 264 ``` 265 2666. (Optional) Implement the blur-and-flip animation. 267 268 The animation is carried out in two phases: blur-and-flip and fade-out blur, where fade-out blur is the same as that in step 5. 269 270 The blur-and-flip animation is realized through two stages of component rotation—initially a 90° rotation outwards, and then a 90° rotation inwards—accompanied by additional effects such as blur, opacity changes, and scaling. 271 272 To ensure that the preview stream is not exposed during flip, you must also build a blackout component to mask the XComponent, following the instructions provided in step 2 in [Blackout Animation](#blackout-animation). 273 274 ```ts 275 /** 276 * A 90° rotation outwards first for transition between the front and real cameras. 277 */ 278 async function rotateFirstAnim() { 279 console.info('rotateFirstAnim E'); 280 // Obtain the surface snapshot. 281 let shotPixel = BlurAnimateUtil.getSurfaceShot(); 282 // Switch from the rear camera to the front camera. 283 if (this.curPosition === 1) { 284 console.info('rotateFirstAnim BACK'); 285 // For candy bar phones, a 90° rotation compensation is applied to the content for a snapshot when switching from the rear camera to the front camera. 286 await shotPixel.rotate(90); // Provided by Image Kit for image content rotation. 287 // For candy bar phones, a 0° rotation compensation is applied to the component for a snapshot when switching from the rear camera to the front camera. 288 this.shotImgRotation = { y: 0.5, angle: 0 }; 289 } else { 290 console.info('rotateFirstAnim FRONT'); 291 // For candy bar phones, a 270° rotation compensation is applied to the content for a snapshot when switching from the front camera to the rear camera. 292 await shotPixel.rotate(270); 293 // For candy bar phones, a 180° rotation compensation is applied to the component for a snapshot when switching from the front camera to the rear camera. 294 this.shotImgRotation = { y: 0.5, angle: 180 }; 295 } 296 this.screenshotPixelMap = shotPixel; 297 this.isShowBlack = true; // Show the blackout component to mask the preview stream. 298 this.isShowBlur = true; // Show the snapshot component. 299 animateToImmediately( 300 { 301 duration: 200, 302 delay: 50, // This delay ensures that the component's scaling and blur effects are triggered in prior to the flip effect. 303 curve: curves.cubicBezierCurve(0.20, 0.00, 0.83, 1.00), 304 onFinish: () => { 305 console.info('rotateFirstAnim X'); 306 // Trigger the second-phase flip effect after onFinish. 307 this.rotateSecondAnim(); 308 } 309 }, 310 () => { 311 // Flip outwards. 312 if (this.curPosition === 1) { 313 this.shotImgRotation = { y: 0.5, angle: 90 }; 314 } else { 315 this.shotImgRotation = { y: 0.5, angle: 270 }; 316 } 317 } 318 ) 319 } 320 321 /** 322 * Flip inwards by 90°. 323 */ 324 async function rotateSecondAnim() { 325 console.info('rotateSecondAnim E'); 326 // Obtain the surface snapshot. 327 let shotPixel = BlurAnimateUtil.getSurfaceShot(); 328 // The rear camera is used. 329 if (this.curPosition === 1) { 330 // For candy bar phones, a 90° rotation compensation is applied to the content for a snapshot taken with the rear camera. 331 await shotPixel.rotate(90); 332 // A -90° rotation compensation is applied to the component to ensure that the image is not mirrored after the second-phase flip. 333 this.shotImgRotation = { y: 0.5, angle: 90 }; 334 } else { // The front camera is used. 335 // For candy bar phones, a 270° rotation compensation is applied to the content for a snapshot taken with the front camera. 336 await shotPixel.rotate(270); 337 // For candy bar phones, a 180° rotation compensation is applied to the component for a snapshot taken with the front camera. 338 this.shotImgRotation = { y: 0.5, angle: 180 }; 339 } 340 this.screenshotPixelMap = shotPixel; 341 animateToImmediately( 342 { 343 duration: 200, 344 curve: curves.cubicBezierCurve(0.17, 0.00, 0.20, 1.00), 345 onFinish: () => { 346 console.info('rotateSecondAnim X'); 347 } 348 }, 349 () => { 350 // Flip inwards to the initial state. 351 if (this.curPosition === 1) { 352 this.shotImgRotation = { y: 0.5, angle: 0 }; 353 } else { 354 this.shotImgRotation = { y: 0.5, angle: 180 }; 355 } 356 } 357 ) 358 } 359 360 /** 361 * Flip outwards by 90°. 362 */ 363 function blurFirstAnim() { 364 console.info('blurFirstAnim E'); 365 // Initialize animation parameters. 366 this.shotImgBlur = 0; // No blur. 367 this.shotImgOpacity = 1; // Opaque. 368 this.shotImgScale = { x: 1, y: 1 }; 369 animateToImmediately( 370 { 371 duration: 200, 372 curve: Curve.Sharp, 373 onFinish: () => { 374 console.info('blurFirstAnim X'); 375 this.blurSecondAnim(); 376 } 377 }, 378 () => { 379 // Blur. 380 this.shotImgBlur = 48; 381 // Scale out. 382 this.shotImgScale = { x: 0.75, y: 0.75 }; 383 } 384 ); 385 } 386 387 /** 388 * Flip inwards by 90°. 389 */ 390 function blurSecondAnim() { 391 console.info('blurSecondAnim E'); 392 animateToImmediately( 393 { 394 duration: 200, 395 curve: Curve.Sharp, 396 onFinish: () => { 397 console.info('blurSecondAnim X'); 398 } 399 }, 400 () => { 401 // Scale in to the initial size. 402 this.shotImgScale = { x: 1, y: 1 }; 403 } 404 ) 405 } 406 ``` 407 4087. Trigger the animations on demand. 409 410 For the mode switching animation, once a user touches the mode button, the **doSurfaceShot** API is invoked, the value of **modeChange** bound to the StorageLink is updated, and the **onModeChange** callback is triggered. In this case, the animation starts to play. 411 412 ```ts 413 onModeChange(): void { 414 console.info('onModeChange'); 415 this.showBlurAnim(); 416 } 417 ``` 418 419 For the front/rear camera switching animation, once a user touches the button to switch between the front and rear cameras, the **doSurfaceShot** API is invoked, the value of **switchCamera** bound to the StorageLink is updated, and the **onSwitchCamera** callback is triggered. In this case, the animation starts to play. 420 421 ```ts 422 onSwitchCamera(): void { 423 console.info('onSwitchCamera'); 424 this.blurFirstAnim(); 425 this.rotateFirstAnim(); 426 } 427 ``` 428 429 For the fade-out blur animation, you must listen for the event [on('frameStart')](../../reference/apis-camera-kit/js-apis-camera.md#onframestart) of the preview stream. Once the value of **frameStart** bound to the StorageLink is updated, and the **onFrameStart** callback is triggered, the animation starts. 430 431 ```ts 432 onFrameStart(): void { 433 console.info('onFrameStart'); 434 this.hideBlurAnim(); 435 } 436 ``` 437