1. 中心对称与旋转
    • 功能:选择图形,设置旋转中心和角度,生成旋转后的图形,制作风车图案。
      应用功能:
      选择图形类型(三角形、正方形、圆形、星形)
      拖动画布设置旋转中心
      调整滑块设置旋转角度
      启用风车模式创建对称图案
      选择不同颜色绘制图形
      显示/隐藏网格和旋转中心
      一键重置所有设置
      一键应用中心对称(180°旋转)
      在这里插入图片描述
// 中心对称与旋转应用
// 功能:选择图形,设置旋转中心和角度,生成旋转后的图形,制作风车图案
// 图形类型
type ShapeType = 'triangle' | 'square' | 'circle' | 'star';

// 点数据接口
interface Point {
  x: number;
  y: number;
}

@Entry
@Component
struct RotationSymmetry {
  @State canvasWidth: number = 300;
  @State canvasHeight: number = 300;
  @State centerX: number = 150;
  @State centerY: number = 150;
  @State rotationAngle: number = 45;
  @State shapeType: ShapeType = 'triangle';
  @State isDragging: boolean = false;
  @State showGrid: boolean = true;
  @State showCenter: boolean = true;
  @State symmetryCount: number = 4;
  @State isWindmillMode: boolean = false;
  
  // 颜色选项
  private colors = ['#FF5722', '#2196F3', '#4CAF50', '#FFC107', '#9C27B0', '#FF9800', '#607D8B', '#E91E63'];
  @State currentColor: string = '#2196F3';
  
  build() {
    Column({ space: 15 }) {
      Text('中心对称与旋转')
        .fontSize(26)
        .fontWeight(FontWeight.Bold)
        .textAlign(TextAlign.Center)

      Column() {
        Text('功能介绍')
          .fontSize(18)
          .fontWeight(FontWeight.Medium)
        Text('选择图形,设置旋转中心和角度,生成旋转后的图形,制作风车图案')
          .fontSize(14)
          .fontColor('#666666')
          .textAlign(TextAlign.Center)
      }
      .width('100%')
      .backgroundColor('#E3F2FD')
      .borderRadius(10)
      .padding(15)

      Flex({ direction: FlexDirection.Column, wrap: FlexWrap.Wrap, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
        Canvas(this.canvasContext)
          .width(this.canvasWidth)
          .height(this.canvasHeight)
          .backgroundColor('#FFFFFF')
          .border({ width: 2, color: '#333' })
          .borderRadius(10)
          .onReady(() => {
            this.drawGrid()
          })
          .gesture(
            GestureGroup(GestureMode.Exclusive,
              PanGesture()
                .onActionStart((event) => {
                  this.isDragging = true
                })
                .onActionUpdate((event) => {
                  if (this.isDragging) {
                    this.centerX = event.fingerList[0].localX
                    this.centerY = event.fingerList[0].localY
                    this.drawGrid()
                  }
                })
                .onActionEnd(() => {
                  this.isDragging = false
                })
            )
          )
          .margin({ bottom: 15 })

        Column({ space: 10 }) {
          Text('控制面板')
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .width('100%')
            .textAlign(TextAlign.Center)

          Column({ space: 8 }) {
            Text('选择图形')
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
            
            Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
              Button('三角形')
                .width('48%')
                .height(40)
                .fontSize(14)
                .backgroundColor(this.shapeType === 'triangle' ? '#2196F3' : '#E0E0E0')
                .onClick(() => {
                  this.shapeType = 'triangle'
                  this.drawGrid()
                })
              
              Button('正方形')
                .width('48%')
                .height(40)
                .fontSize(14)
                .backgroundColor(this.shapeType === 'square' ? '#2196F3' : '#E0E0E0')
                .onClick(() => {
                  this.shapeType = 'square'
                  this.drawGrid()
                })
              
              Button('圆形')
                .width('48%')
                .height(40)
                .fontSize(14)
                .backgroundColor(this.shapeType === 'circle' ? '#2196F3' : '#E0E0E0')
                .onClick(() => {
                  this.shapeType = 'circle'
                  this.drawGrid()
                })
              
              Button('星形')
                .width('48%')
                .height(40)
                .fontSize(14)
                .backgroundColor(this.shapeType === 'star' ? '#2196F3' : '#E0E0E0')
                .onClick(() => {
                  this.shapeType = 'star'
                  this.drawGrid()
                })
            }
            .width('100%')
          }
          .width('100%')
          .padding(10)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)

          Column({ space: 8 }) {
            Text('旋转角度')
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
            
            Slider({
              value: this.rotationAngle,
              min: 0,
              max: 360,
              step: 1,
              style: SliderStyle.OutSet
            })
              .width('100%')
              .blockColor('#2196F3')
              .trackColor('#E0E0E0')
              .selectedColor('#2196F3')
              .onChange((value: number) => {
                this.rotationAngle = value
                this.drawGrid()
              })
            
            Text(`当前角度: ${Math.round(this.rotationAngle)}°`)
              .fontSize(12)
              .fontColor('#666666')
          }
          .width('100%')
          .padding(10)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)

          Column({ space: 8 }) {
            Text('画笔颜色')
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
            
            Flex({ wrap: FlexWrap.Wrap, justifyContent: FlexAlign.SpaceBetween }) {
              ForEach(this.colors, (color: string) => {
                Row()
                  .width(35)
                  .height(35)
                  .backgroundColor(color)
                  .borderRadius(8)
                  .border({ width: this.currentColor === color ? 3 : 1, color: this.currentColor === color ? '#333' : '#DDD' })
                  .onClick(() => {
                    this.currentColor = color
                    this.drawGrid()
                  })
              })
            }
            .width('100%')
          }
          .width('100%')
          .padding(10)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)

          Column({ space: 8 }) {
            Text('风车模式')
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
            
            Row({ space: 10 }) {
              Row({ space: 5 }) {
                Checkbox({ name: 'windmill', group: 'mode' })
                  .select(this.isWindmillMode)
                  .onChange((value: boolean) => {
                    this.isWindmillMode = value
                    this.drawGrid()
                  })
                Text('启用')
                  .fontSize(14)
              }
            }
          }
          .width('100%')
          .padding(10)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)

          Column({ space: 8 }) {
            Text('对称次数 (风车模式)')
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
            
            Slider({
              value: this.symmetryCount,
              min: 2,
              max: 12,
              step: 1,
              style: SliderStyle.OutSet
            })
              .width('100%')
              .blockColor('#2196F3')
              .trackColor('#E0E0E0')
              .selectedColor('#2196F3')
              .onChange((value: number) => {
                this.symmetryCount = Math.round(value)
                this.drawGrid()
              })
            
            Text(`当前次数: ${this.symmetryCount}`)
              .fontSize(12)
              .fontColor('#666666')
          }
          .width('100%')
          .padding(10)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)

          Column({ space: 8 }) {
            Text('显示选项')
              .fontSize(14)
              .fontWeight(FontWeight.Medium)
            
            Row({ space: 10 }) {
              Row({ space: 5 }) {
                Checkbox({ name: 'grid', group: 'display' })
                  .select(this.showGrid)
                  .onChange((value: boolean) => {
                    this.showGrid = value
                    this.drawGrid()
                  })
                Text('网格')
                  .fontSize(14)
              }
              
              Row({ space: 5 }) {
                Checkbox({ name: 'center', group: 'display' })
                  .select(this.showCenter)
                  .onChange((value: boolean) => {
                    this.showCenter = value
                    this.drawGrid()
                  })
                Text('旋转中心')
                  .fontSize(14)
              }
            }
          }
          .width('100%')
          .padding(10)
          .backgroundColor('#F5F5F5')
          .borderRadius(8)

          Row({ space: 10 }) {
            Button('重置')
              .width('48%')
              .height(45)
              .fontSize(16)
              .backgroundColor('#FF9800')
              .onClick(() => {
                this.reset()
              })
            
            Button('中心对称')
              .width('48%')
              .height(45)
              .fontSize(16)
              .backgroundColor('#4CAF50')
              .onClick(() => {
                this.rotationAngle = 180
                this.drawGrid()
              })
          }
          .width('100%')
        }
        .width('90%')
        .padding(10)
        .backgroundColor('#FAFAFA')
        .borderRadius(10)
      }

      Column({ space: 8 }) {
        Text('使用说明')
          .fontSize(16)
          .fontWeight(FontWeight.Bold)
        
        Text('• 点击选择图形类型')
          .fontSize(14)
          .fontColor('#666666')
        
        Text('• 拖动画布设置旋转中心')
          .fontSize(14)
          .fontColor('#666666')
        
        Text('• 调整滑块设置旋转角度')
          .fontSize(14)
          .fontColor('#666666')
        
        Text('• 启用风车模式创建对称图案')
          .fontSize(14)
          .fontColor('#666666')
      }
      .width('95%')
      .padding(12)
      .backgroundColor('#FFF3E0')
      .borderRadius(10)
    }
    .width('100%')
    .height('100%')
    .padding(10)
    .justifyContent(FlexAlign.Start)
  }

  private canvasContext: CanvasRenderingContext2D = new CanvasRenderingContext2D();

  private drawGrid() {
    const ctx = this.canvasContext
    ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
    
    if (this.showGrid) {
      ctx.strokeStyle = '#E0E0E0'
      ctx.lineWidth = 1
      
      const gridSize = 20
      for (let i = 0; i <= this.canvasWidth; i += gridSize) {
        ctx.beginPath()
        ctx.moveTo(i, 0)
        ctx.lineTo(i, this.canvasHeight)
        ctx.stroke()
      }
      
      for (let i = 0; i <= this.canvasHeight; i += gridSize) {
        ctx.beginPath()
        ctx.moveTo(0, i)
        ctx.lineTo(this.canvasWidth, i)
        ctx.stroke()
      }
    }
    
    if (this.showCenter) {
      ctx.strokeStyle = '#F44336'
      ctx.lineWidth = 2
      ctx.fillStyle = '#F44336'
      
      ctx.beginPath()
      ctx.arc(this.centerX, this.centerY, 6, 0, 2 * Math.PI)
      ctx.fill()
      ctx.stroke()
    }
    
    if (this.isWindmillMode) {
      this.drawWindmill()
    } else {
      this.drawOriginalShape()
      this.drawRotatedShape()
    }
  }

  private drawOriginalShape() {
    const ctx = this.canvasContext
    ctx.strokeStyle = this.currentColor
    ctx.fillStyle = this.currentColor
    ctx.lineWidth = 2
    
    const size = 60
    const x = this.centerX - size / 2
    const y = this.centerY - size / 2
    
    ctx.globalAlpha = 0.3
    this.drawShape(ctx, x, y, size, 0)
    ctx.globalAlpha = 1
  }

  private drawRotatedShape() {
    const ctx = this.canvasContext
    ctx.strokeStyle = this.currentColor
    ctx.fillStyle = this.currentColor
    ctx.lineWidth = 2
    
    const size = 60
    const angleRad = (this.rotationAngle * Math.PI) / 180
    
    ctx.save()
    ctx.translate(this.centerX, this.centerY)
    ctx.rotate(angleRad)
    this.drawShape(ctx, -size / 2, -size / 2, size, 0)
    ctx.restore()
  }

  private drawWindmill() {
    const ctx = this.canvasContext
    ctx.lineWidth = 2
    
    const size = 60
    const angleStep = (360 / this.symmetryCount) * Math.PI / 180
    
    for (let i = 0; i < this.symmetryCount; i++) {
      const angleRad = angleStep * i
      const colorIndex = i % this.colors.length
      
      ctx.strokeStyle = this.colors[colorIndex]
      ctx.fillStyle = this.colors[colorIndex]
      
      ctx.save()
      ctx.translate(this.centerX, this.centerY)
      ctx.rotate(angleRad)
      this.drawShape(ctx, -size / 2, -size / 2, size, 0)
      ctx.restore()
    }
  }

  private drawShape(ctx: CanvasRenderingContext2D, x: number, y: number, size: number, angle: number) {
    switch (this.shapeType) {
      case 'triangle':
        this.drawTriangle(ctx, x, y, size)
        break
      case 'square':
        this.drawSquare(ctx, x, y, size)
        break
      case 'circle':
        this.drawCircle(ctx, x, y, size)
        break
      case 'star':
        this.drawStar(ctx, x, y, size)
        break
    }
  }

  private drawTriangle(ctx: CanvasRenderingContext2D, x: number, y: number, size: number) {
    ctx.beginPath()
    ctx.moveTo(x + size / 2, y)
    ctx.lineTo(x + size, y + size)
    ctx.lineTo(x, y + size)
    ctx.closePath()
    ctx.fill()
    ctx.stroke()
  }

  private drawSquare(ctx: CanvasRenderingContext2D, x: number, y: number, size: number) {
    ctx.beginPath()
    ctx.rect(x, y, size, size)
    ctx.fill()
    ctx.stroke()
  }

  private drawCircle(ctx: CanvasRenderingContext2D, x: number, y: number, size: number) {
    ctx.beginPath()
    ctx.arc(x + size / 2, y + size / 2, size / 2, 0, 2 * Math.PI)
    ctx.fill()
    ctx.stroke()
  }

  private drawStar(ctx: CanvasRenderingContext2D, x: number, y: number, size: number) {
    ctx.beginPath()
    for (let i = 0; i < 5; i++) {
      const angle = (i * 2 * Math.PI) / 5 - Math.PI / 2
      const outerX = x + size / 2 + (size / 2) * Math.cos(angle)
      const outerY = y + size / 2 + (size / 2) * Math.sin(angle)
      
      const innerAngle = ((i + 0.5) * 2 * Math.PI) / 5 - Math.PI / 2
      const innerX = x + size / 2 + (size / 4) * Math.cos(innerAngle)
      const innerY = y + size / 2 + (size / 4) * Math.sin(innerAngle)
      
      if (i === 0) {
        ctx.moveTo(outerX, outerY)
      } else {
        ctx.lineTo(outerX, outerY)
      }
      ctx.lineTo(innerX, innerY)
    }
    ctx.closePath()
    ctx.fill()
    ctx.stroke()
  }

  private reset() {
    this.centerX = 150
    this.centerY = 150
    this.rotationAngle = 45
    this.shapeType = 'triangle'
    this.currentColor = '#2196F3'
    this.symmetryCount = 4
    this.isWindmillMode = false
    this.drawGrid()
  }
}
Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐