当前位置: 首页 > news >正文

制作大风车动画

这个案例的风车旋转应用了图形变换来实现,速度和缩放比例应用slider来实现,其中图片的速度,图片大小的信息通过@State来定义变量管理,速度和和缩放比例的即时的值通过@Prop来管理。

1. 案例效果截图

2. 案例运用到的知识点

2.1. 核心知识点

  • Text组件:文本组件,用于呈现一段信息。
  • Image组件:图片组件,用来渲染展示图片。
  • Slider组件:滑动条组件,用来快速调节设置值,如音量、亮度等。

2.2. 其他知识点

  • ArkTS语言基础
  • 自定义组件和组件生命周期
  • V1状态管理:@State/@Prop
  • 内置组件:Column/Image/Text/Row/Stack/Blank/Button
  • 常量与资源分类的访问

3. 代码结构

├──entry/src/main/ets             // 代码区      
│  ├──common                        
│  │  └──Constants.ets            // 常量
│  ├──entryability
│  │  └──EntryAbility.ts          // 应用的入口
│  ├──pages
│  │  └──SliderPage.ets           // 入口页面
│  └──view                         
│     └──PanelComponent.ets       // 自定义组件
└──entry/src/main/resources       // 资源文件目录

4. 公共文件与资源

本案例涉及到的常量类和工具类代码如下:

4.1. 通用常量类

// entry/src/main/ets/common/Constant.ets
export enum RotatePosition {X = 0,Y = 0,Z = 1,
}export enum SliderSpeed {MIN = 1,MAX = 10,STEP = 1,
}export enum SliderMode {SPEED = 1,SCALE = 2,
}export class Constants {static readonly FONT_SIZE = 14static readonly LAYOUT_WEIGHT = 1static readonly PERCENTAGE_100 = '100%'static readonly DELAY_TIME = 15static readonly SLIDER_SKIN = $r('app.color.slider_color')static readonly INTERVAL = 0static readonly SPEED = 5static readonly WEIGHT_BLANK_IMAGE = '25%'static readonly PANEL_MARGIN_TOP = '4%'static readonly PANEL_MARGIN_BOTTOM = '5%'static readonly IMAGE_SIZE = 150static readonly ANGLE = 0static readonly IMAGE_SIZE_INITIAL = 1static readonly FRACTION_DIGITS = 1static readonly TITLE_PADDING = 5static readonly TITLE_MARGIN_HORIZONTAL = 10static readonly SPEED_MARGIN_BOTTOM = 6static readonly SLIDER_MARGIN_HORIZONTAL = 11static readonly PANEL_RADIUS = 24static readonly PANEL_IMAGE_WIDTH = 19static readonly PANEL_IMAGE_HEIGHT = 16static readonly PANEL_IMAGE_BIG_HEIGHT = 18static readonly PANEL_IMAGE_BIG_WIDTH = 22static readonly PANEL_WIDTH = '98%'static readonly PANEL_FONT_SIZE = 20static readonly PANEL_END_FONT_SIZE = 24static readonly PANEL_HOLDER = 'A'static readonly PANEL_HEIGHT = 100static readonly PANEL_PADDING = 10static readonly PANEL_MARGIN = 10static readonly MIN: number = 0.5static readonly MAX: number = 2.5static readonly STEP: number = 0.1
}

本案例涉及到的资源文件如下:

4.2. string.json

// entry/src/main/resources/base/element/string.json
{"string": [{"name": "module_desc","value": "module description"},{"name": "EntryAbility_desc","value": "description"},{"name": "EntryAbility_label","value": "label"},{"name": "scale_text","value": "缩放比例"},{"name": "speed_text","value": "速度"}]
}

4.3. color.json

// entry/src/main/resources/base/element/color.json
{"color": [{"name": "start_window_background","value": "#FFFFFF"},{"name": "white","value": "#FFFFFF"},{"name": "slider_color","value": "#007dff"},{"name": "background_color","value": "#F1F3F5"},{"name": "font_color","value": "#182431"}]
}

其他资源请到源码中获取。

5. 单个页面扁平实现

// entry/src/main/ets/pages/Index.ets
import { Constants, RotatePosition, SliderMode, SliderSpeed
} from '../common/Constants'@Entry
@Component
struct Index {@State private speed: number = Constants.SPEED@State private imageSize: number = Constants.IMAGE_SIZE_INITIAL@State private angle: number = Constants.ANGLEprivate interval: number = Constants.INTERVALbuild() {Column() {Image($rawfile('windmill.png')).objectFit(ImageFit.Contain).height(Constants.IMAGE_SIZE).width(Constants.IMAGE_SIZE).rotate({x: RotatePosition.X,y: RotatePosition.Y,z: RotatePosition.Z,angle: this.angle}).scale({ x: this.imageSize, y: this.imageSize }).margin({ bottom: Constants.WEIGHT_BLANK_IMAGE })Column() {Text($r('app.string.speed_text')).width(Constants.PANEL_WIDTH).padding({ left: Constants.TITLE_PADDING }).fontSize(Constants.FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({left: Constants.TITLE_MARGIN_HORIZONTAL,right: Constants.TITLE_MARGIN_HORIZONTAL})Column() {Text(this.speed.toFixed(Constants.FRACTION_DIGITS)).fontSize(Constants.FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({ bottom: Constants.SPEED_MARGIN_BOTTOM })Row() {Image($rawfile('speedLow.png')).objectFit(ImageFit.Contain).height(Constants.PANEL_IMAGE_HEIGHT).width(Constants.PANEL_IMAGE_WIDTH)Slider({value: this.speed,min: SliderSpeed.MIN,max: SliderSpeed.MAX,step: SliderSpeed.STEP,style: SliderStyle.InSet}).layoutWeight(Constants.LAYOUT_WEIGHT).selectedColor(Constants.SLIDER_SKIN).onChange((value: number) => {this.speed = valueclearInterval(this.interval)this.speedChange()}).margin({left: Constants.SLIDER_MARGIN_HORIZONTAL,right: Constants.SLIDER_MARGIN_HORIZONTAL})Image($rawfile('speed.png')).objectFit(ImageFit.Contain).height(Constants.PANEL_IMAGE_BIG_HEIGHT).width(Constants.PANEL_IMAGE_BIG_WIDTH).height(Constants.PANEL_IMAGE_BIG_HEIGHT).width(Constants.PANEL_IMAGE_BIG_WIDTH)}}.justifyContent(FlexAlign.Center).backgroundColor(Color.White).borderRadius(Constants.PANEL_RADIUS).height(Constants.PANEL_HEIGHT).width(Constants.PANEL_WIDTH).padding({left: Constants.PANEL_PADDING,right: Constants.PANEL_PADDING}).margin({top: Constants.PANEL_MARGIN,bottom: Constants.PANEL_MARGIN})}.padding({left: Constants.PANEL_PADDING,right: Constants.PANEL_PADDING}).width(Constants.PERCENTAGE_100).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)Column() {Text($r('app.string.scale_text')).width(Constants.PANEL_WIDTH).padding({ left: Constants.TITLE_PADDING }).fontSize(Constants.FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({left: Constants.TITLE_MARGIN_HORIZONTAL,right: Constants.TITLE_MARGIN_HORIZONTAL})Column() {Text(this.imageSize.toFixed(Constants.FRACTION_DIGITS)).fontSize(Constants.FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({ bottom: Constants.SPEED_MARGIN_BOTTOM })Row() {Text(Constants.PANEL_HOLDER).fontSize(Constants.PANEL_FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({ bottom: Constants.SPEED_MARGIN_BOTTOM })Slider({value: this.imageSize,min: Constants.MIN,max: Constants.MAX,step: Constants.STEP,style: SliderStyle.InSet}).layoutWeight(Constants.LAYOUT_WEIGHT).selectedColor(Constants.SLIDER_SKIN).onChange((value: number) => {this.imageSize = value}).margin({left: Constants.SLIDER_MARGIN_HORIZONTAL,right: Constants.SLIDER_MARGIN_HORIZONTAL})Text(Constants.PANEL_HOLDER).fontSize(Constants.PANEL_END_FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({ bottom: Constants.SPEED_MARGIN_BOTTOM })}}.justifyContent(FlexAlign.Center).backgroundColor(Color.White).borderRadius(Constants.PANEL_RADIUS).height(Constants.PANEL_HEIGHT).width(Constants.PANEL_WIDTH).padding({left: Constants.PANEL_PADDING,right: Constants.PANEL_PADDING}).margin({top: Constants.PANEL_MARGIN,bottom: Constants.PANEL_MARGIN})}.padding({left: Constants.PANEL_PADDING,right: Constants.PANEL_PADDING}).width(Constants.PERCENTAGE_100).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}.justifyContent(FlexAlign.End).height(Constants.PERCENTAGE_100).width(Constants.PERCENTAGE_100).backgroundColor($r('app.color.background_color'))}speedChange(): void {let that = thisthis.angle = Constants.ANGLEthis.interval = setInterval(() => {that.angle += that.speed}, Constants.DELAY_TIME)}onPageShow() {clearInterval(this.interval)this.speedChange()}
}

6. 组件抽离实现

6.1. 面板组件

// entry/src/main/ets/views/PanelComponent.ets
import { Constants, SliderMode } from '../common/Constants'@Component
export struct PanelComponent {@Prop text: string = ''title?: Resourcemode?: SliderModeoptions?: SliderOptionscallback: (value: number, mode: SliderChangeMode) => void = () => {}build() {Column() {Text(this.title).width(Constants.PANEL_WIDTH).padding({ left: Constants.TITLE_PADDING }).fontSize(Constants.FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({left: Constants.TITLE_MARGIN_HORIZONTAL,right: Constants.TITLE_MARGIN_HORIZONTAL})Column() {Text(this.text).fontSize(Constants.FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({ bottom: Constants.SPEED_MARGIN_BOTTOM })Row() {if (this.mode === SliderMode.SPEED) {Image($rawfile('speedLow.png')).objectFit(ImageFit.Contain).height(Constants.PANEL_IMAGE_HEIGHT).width(Constants.PANEL_IMAGE_WIDTH)} else {Text(Constants.PANEL_HOLDER).fontSize(Constants.PANEL_FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({ bottom: Constants.SPEED_MARGIN_BOTTOM })}Slider(this.options).layoutWeight(Constants.LAYOUT_WEIGHT).selectedColor(Constants.SLIDER_SKIN).onChange((value: number, mode: SliderChangeMode) => {this.callback(value, mode);}).margin({left: Constants.SLIDER_MARGIN_HORIZONTAL,right: Constants.SLIDER_MARGIN_HORIZONTAL})if (this.mode === SliderMode.SPEED) {Image($rawfile('speed.png')).objectFit(ImageFit.Contain).height(Constants.PANEL_IMAGE_BIG_HEIGHT).width(Constants.PANEL_IMAGE_BIG_WIDTH).height(Constants.PANEL_IMAGE_BIG_HEIGHT).width(Constants.PANEL_IMAGE_BIG_WIDTH)} else {Text(Constants.PANEL_HOLDER).fontSize(Constants.PANEL_END_FONT_SIZE).fontWeight(FontWeight.Medium).fontColor($r('app.color.font_color')).margin({ bottom: Constants.SPEED_MARGIN_BOTTOM })}}}.justifyContent(FlexAlign.Center).backgroundColor(Color.White).borderRadius(Constants.PANEL_RADIUS).height(Constants.PANEL_HEIGHT).width(Constants.PANEL_WIDTH).padding({left: Constants.PANEL_PADDING,right: Constants.PANEL_PADDING}).margin({top: Constants.PANEL_MARGIN,bottom: Constants.PANEL_MARGIN})}.padding({left: Constants.PANEL_PADDING,right: Constants.PANEL_PADDING}).width(Constants.PERCENTAGE_100).alignItems(HorizontalAlign.Center).justifyContent(FlexAlign.Center)}
}

6.2. 页面改造

// entry/src/main/ets/pages/Index.ets
import { Constants, RotatePosition, SliderMode, SliderSpeed
} from '../common/Constants'
import { PanelComponent } from '../views/PanelComponent'@Entry
@Component
struct SliderPage {@State private speed: number = Constants.SPEED@State private imageSize: number = Constants.IMAGE_SIZE_INITIAL@State private angle: number = Constants.ANGLEprivate interval: number = Constants.INTERVALbuild() {Column() {Image($rawfile('windmill.png')).objectFit(ImageFit.Contain).height(Constants.IMAGE_SIZE).width(Constants.IMAGE_SIZE).rotate({x: RotatePosition.X,y: RotatePosition.Y,z: RotatePosition.Z,angle: this.angle}).scale({ x: this.imageSize, y: this.imageSize }).margin({ bottom: Constants.WEIGHT_BLANK_IMAGE })PanelComponent({mode: SliderMode.SPEED,title: $r('app.string.speed_text'),text: this.speed.toFixed(Constants.FRACTION_DIGITS),callback: ((value: number) => {this.speed = valueclearInterval(this.interval)this.speedChange()}),options: {value: this.speed,min: SliderSpeed.MIN,max: SliderSpeed.MAX,step: SliderSpeed.STEP,style: SliderStyle.InSet}})PanelComponent({mode: SliderMode.SCALE,title: $r('app.string.scale_text'),text: this.imageSize.toFixed(Constants.FRACTION_DIGITS),callback: ((value: number) => {this.imageSize = value}),options: {value: this.imageSize,min: Constants.MIN,max: Constants.MAX,step: Constants.STEP,style: SliderStyle.InSet}}).margin({bottom: Constants.PANEL_MARGIN_BOTTOM,top: Constants.PANEL_MARGIN_TOP})}.justifyContent(FlexAlign.End).height(Constants.PERCENTAGE_100).backgroundColor($r('app.color.background_color'))}speedChange(): void {let that = thisthis.angle = Constants.ANGLEthis.interval = setInterval(() => {that.angle += that.speed}, Constants.DELAY_TIME)}onPageShow() {clearInterval(this.interval)this.speedChange()}
}

7. 代码与视频教程

完整案例代码与视频教程请参见:

代码:Code-05-02.zip。

视频:《大风车吱扭扭的转》。

相关文章:

  • Mipsel固件Fuzzing小记
  • 永磁同步电机公式总结——反电动势、磁链、转矩公式;三项、两项电压方程;坐标表换方程
  • Node.js 源码架构详解
  • 【磁盘清理】C盘告急,给JetBrains缓存文件搬个家
  • <input type=“number“>不显示的原因
  • NBA足球赛事直播源码体育直播M33模板赛事源码
  • CSS- 4.1 浮动(Float)
  • 【Linux笔记】——线程互斥与互斥锁的封装
  • 本地缓存更新方案探索
  • 产品经理入门(2)产品体验报告
  • Ubuntu 环境下搭建 Docker 及常见问题解决方案
  • 大数据会被AI取代?不!大数据才是AI的“智慧燃料”引擎
  • mcp学习笔记
  • JSP链接MySQL8.0(Eclipse+Tomcat9.0+MySQL8.0)
  • 西门子 Teamcenter13 Eclipse RCP 开发 1.2 工具栏 开关按钮
  • 在线教育本地化分发:代理IP实现区域访问控制与内容适配
  • MySQL表的约束(上)
  • 嵌入式学习笔记 - STM32定时器的输入通道与时钟源
  • Vue+Vite学习笔记
  • C语言查漏补缺
  • 广西隆林突发山洪,致3人遇难1人失联
  • 上海公办小学验证今起开始,下周一和周二分区进行民办摇号
  • 博物馆日|为一个展奔赴一座城!上海171家博物馆等你来
  • 特朗普公开“怼”库克:苹果不应在印度生产手机
  • 美国关税压力下,日本经济一年来首次萎缩
  • 2000多年前的“新衣”长这样!马王堆文物研究新成果上新