HARMONYOS应用实例254:中心对称与旋转
·
- 中心对称与旋转
- 功能:选择图形,设置旋转中心和角度,生成旋转后的图形,制作风车图案。
应用功能:
选择图形类型(三角形、正方形、圆形、星形)
拖动画布设置旋转中心
调整滑块设置旋转角度
启用风车模式创建对称图案
选择不同颜色绘制图形
显示/隐藏网格和旋转中心
一键重置所有设置
一键应用中心对称(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()
}
}
更多推荐



所有评论(0)