개요

Transform은 자식 위젯을 그리기 전에 변형을 적용하는 위젯입니다. Flutter의 Transform에서 영감을 받았으며, Matrix4를 사용하여 회전, 크기 조절, 이동, 기울이기 등의 다양한 변형을 적용할 수 있습니다.

RotatedBox와 달리 Transform은 레이아웃 전이 아닌 그리기 직전에 변형을 적용하므로, 위젯이 차지하는 공간 계산에는 변형이 고려되지 않습니다. 이로 인해 성능상 이점이 있지만 레이아웃에 주의가 필요합니다.

참고: https://api.flutter.dev/flutter/widgets/Transform-class.html

언제 사용하나요?

  • 위젯을 회전시켜야 할 때
  • 위젯의 크기를 확대/축소해야 할 때
  • 위젯을 이동(평행이동)시켜야 할 때
  • 3D 효과나 원근법이 필요할 때
  • 애니메이션 효과를 만들 때
  • 시각적 효과나 장식이 필요할 때

기본 사용법

import { Transform, Matrix4, Container } from '@meursyphus/flitter';

// 45도 회전
const RotatedBox = Transform.rotate({
  angle: Math.PI / 4,  // 라디안 단위
  child: Container({
    width: 100,
    height: 100,
    color: '#3498db'
  })
});

// 크기 조절
const ScaledBox = Transform.scale({
  scale: 1.5,  // 1.5배 확대
  child: Container({
    width: 100,
    height: 100,
    color: '#e74c3c'
  })
});

// 이동
const TranslatedBox = Transform.translate({
  offset: new Offset({ x: 50, y: 30 }),
  child: Container({
    width: 100,
    height: 100,
    color: '#2ecc71'
  })
});

Props

transform (필수)

값: Matrix4

적용할 변형을 나타내는 4x4 변형 행렬입니다. Matrix4 클래스의 다양한 정적 메서드를 사용하여 생성할 수 있습니다.

// 이동 변형
transform: Matrix4.translationValues(50, 30, 0)

// 크기 조절 변형
transform: Matrix4.diagonal3Values(2.0, 1.5, 1.0)  // X축 2배, Y축 1.5배

// 기울이기 변형
transform: Matrix4.skewX(0.5)  // X축 기울이기

origin (선택)

값: Offset

변형의 기준점을 지정합니다. 지정하지 않으면 alignment에 따라 결정됩니다.

// 왼쪽 상단 모서리를 기준으로 회전
origin: new Offset({ x: 0, y: 0 })

// 우측 하단 모서리를 기준으로 회전
origin: new Offset({ x: 100, y: 100 })

alignment (선택)

값: Alignment (기본값: Alignment.center)

변형의 정렬 기준점을 지정합니다. origin이 지정되지 않았을 때 사용됩니다.

// 중앙 기준 (기본값)
alignment: Alignment.center

// 왼쪽 상단 기준
alignment: Alignment.topLeft

// 우측 하단 기준
alignment: Alignment.bottomRight

child (선택)

값: Widget

변형이 적용될 자식 위젯입니다.

편의 메서드

Transform 위젯은 일반적인 변형을 쉽게 적용할 수 있는 정적 메서드들을 제공합니다:

Transform.rotate()

Transform.rotate({
  angle: number,          // 회전 각도(라디안)
  origin?: Offset,        // 회전 기준점
  alignment?: Alignment,  // 정렬 기준
  child?: Widget
})

Transform.scale()

Transform.scale({
  scale?: number,         // 균등 크기 조절
  scaleX?: number,        // X축 크기 조절
  scaleY?: number,        // Y축 크기 조절
  origin?: Offset,        // 크기 조절 기준점
  alignment?: Alignment,  // 정렬 기준
  child?: Widget
})

Transform.translate()

Transform.translate({
  offset: Offset,         // 이동 거리
  child?: Widget
})

실제 사용 예제

예제 1: 다양한 회전 변형

import { Transform, Container, Row, Column, Text } from '@meursyphus/flitter';

const RotationExamples = Row({
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    Column({
      children: [
        Transform.rotate({
          angle: Math.PI / 6,  // 30도
          child: Container({
            width: 80,
            height: 80,
            color: '#e74c3c',
            child: Center({
              child: Text('30°', { style: { color: 'white' } })
            })
          })
        }),
        Text('30도 회전')
      ]
    }),
    
    Column({
      children: [
        Transform.rotate({
          angle: Math.PI / 4,  // 45도
          child: Container({
            width: 80,
            height: 80,
            color: '#3498db',
            child: Center({
              child: Text('45°', { style: { color: 'white' } })
            })
          })
        }),
        Text('45도 회전')
      ]
    }),
    
    Column({
      children: [
        Transform.rotate({
          angle: Math.PI / 2,  // 90도
          child: Container({
            width: 80,
            height: 80,
            color: '#2ecc71',
            child: Center({
              child: Text('90°', { style: { color: 'white' } })
            })
          })
        }),
        Text('90도 회전')
      ]
    })
  ]
});

예제 2: 크기 조절 변형

import { Transform, Container, Row } from '@meursyphus/flitter';

const ScaleExamples = Row({
  mainAxisAlignment: MainAxisAlignment.spaceEvenly,
  children: [
    // 균등 확대
    Transform.scale({
      scale: 1.5,
      child: Container({
        width: 60,
        height: 60,
        color: '#9b59b6'
      })
    }),
    
    // X축만 확대
    Transform.scale({
      scaleX: 2.0,
      scaleY: 1.0,
      child: Container({
        width: 60,
        height: 60,
        color: '#e67e22'
      })
    }),
    
    // Y축만 확대
    Transform.scale({
      scaleX: 1.0,
      scaleY: 2.0,
      child: Container({
        width: 60,
        height: 60,
        color: '#1abc9c'
      })
    }),
    
    // 축소
    Transform.scale({
      scale: 0.5,
      child: Container({
        width: 60,
        height: 60,
        color: '#f39c12'
      })
    })
  ]
});

예제 3: 복합 변형 (회전 + 크기 조절)

import { Transform, Matrix4, Container } from '@meursyphus/flitter';

const CombinedTransform = () => {
  // Matrix4를 직접 조작하여 복합 변형 생성
  const matrix = Matrix4.identity();
  matrix.translate(50, 0, 0);           // 이동
  matrix.rotateZ(Math.PI / 4);          // 45도 회전
  matrix.scale(1.5, 1.5, 1.0);         // 1.5배 확대
  
  return Transform({
    transform: matrix,
    child: Container({
      width: 80,
      height: 80,
      color: '#e74c3c',
      child: Center({
        child: Text('복합\n변형', {
          style: { 
            color: 'white', 
            fontSize: 12,
            textAlign: 'center'
          }
        })
      })
    })
  });
};

예제 4: 3D 원근법 효과

import { Transform, Matrix4, Container } from '@meursyphus/flitter';

const PerspectiveCard = Transform({
  transform: (() => {
    const matrix = Matrix4.identity();
    matrix.setEntry(3, 2, 0.001);  // 원근법 효과
    matrix.rotateX(Math.PI / 6);   // X축 30도 회전
    return matrix;
  })(),
  alignment: Alignment.center,
  child: Container({
    width: 200,
    height: 120,
    decoration: new BoxDecoration({
      color: '#34495e',
      borderRadius: BorderRadius.circular(12),
      boxShadow: [
        new BoxShadow({
          color: 'rgba(0, 0, 0, 0.3)',
          offset: new Offset({ x: 0, y: 8 }),
          blurRadius: 16
        })
      ]
    }),
    child: Center({
      child: Text('3D 카드', {
        style: { 
          color: 'white', 
          fontSize: 18,
          fontWeight: 'bold'
        }
      })
    })
  })
});

예제 5: 애니메이션과 함께 사용

import { Transform, AnimationController, Tween } from '@meursyphus/flitter';

class AnimatedTransform extends StatefulWidget {
  createState() {
    return new AnimatedTransformState();
  }
}

class AnimatedTransformState extends State {
  animationController!: AnimationController;
  rotationTween!: Tween<number>;
  scaleTween!: Tween<number>;
  
  initState() {
    this.animationController = new AnimationController({
      duration: 2000
    });
    
    this.rotationTween = new Tween({
      begin: 0,
      end: Math.PI * 2
    });
    
    this.scaleTween = new Tween({
      begin: 0.5,
      end: 1.5
    });
    
    this.animationController.addListener(() => this.setState());
    this.animationController.repeat({ reverse: true });
  }
  
  dispose() {
    this.animationController.dispose();
  }
  
  build() {
    const rotation = this.rotationTween.evaluate(this.animationController);
    const scale = this.scaleTween.evaluate(this.animationController);
    
    return Transform({
      transform: (() => {
        const matrix = Matrix4.identity();
        matrix.rotateZ(rotation);
        matrix.scale(scale, scale, 1.0);
        return matrix;
      })(),
      alignment: Alignment.center,
      child: Container({
        width: 100,
        height: 100,
        decoration: new BoxDecoration({
          color: '#3498db',
          borderRadius: BorderRadius.circular(12)
        }),
        child: Center({
          child: Text('애니메이션', {
            style: { color: 'white', fontWeight: 'bold' }
          })
        })
      })
    });
  }
}

예제 6: 카드 뒤집기 효과

import { Transform, Matrix4, GestureDetector } from '@meursyphus/flitter';

class FlipCard extends StatefulWidget {
  createState() {
    return new FlipCardState();
  }
}

class FlipCardState extends State {
  isFlipped = false;
  
  toggleFlip = () => {
    this.setState(() => {
      this.isFlipped = !this.isFlipped;
    });
  };
  
  build() {
    return GestureDetector({
      onTap: this.toggleFlip,
      child: Transform({
        transform: (() => {
          const matrix = Matrix4.identity();
          matrix.rotateY(this.isFlipped ? Math.PI : 0);
          return matrix;
        })(),
        alignment: Alignment.center,
        child: Container({
          width: 200,
          height: 120,
          decoration: new BoxDecoration({
            color: this.isFlipped ? '#e74c3c' : '#3498db',
            borderRadius: BorderRadius.circular(12),
            boxShadow: [
              new BoxShadow({
                color: 'rgba(0, 0, 0, 0.2)',
                offset: new Offset({ x: 0, y: 4 }),
                blurRadius: 8
              })
            ]
          }),
          child: Center({
            child: Text(
              this.isFlipped ? '뒷면' : '앞면',
              { 
                style: { 
                  color: 'white', 
                  fontSize: 18,
                  fontWeight: 'bold'
                }
              }
            )
          })
        })
      })
    });
  }
}

주의사항

  • 레이아웃 영향 없음: Transform은 위젯이 차지하는 공간에 영향을 주지 않으므로 다른 위젯과 겹칠 수 있습니다
  • 성능: 복잡한 변형이나 많은 Transform 위젯은 성능에 영향을 줄 수 있습니다
  • 각도 단위: 회전 각도는 라디안 단위를 사용합니다 (도수 * Math.PI / 180)
  • 기준점 이해: origin과 alignment의 차이를 이해하고 적절히 사용하세요
  • 3D 변형: Z축 변형 시 원근법 설정이 필요할 수 있습니다
  • 애니메이션: 부드러운 변형 애니메이션을 위해 AnimationController와 함께 사용하세요

관련 위젯

  • AnimatedContainer: 자동으로 애니메이션되는 변형이 필요할 때
  • RotatedBox: 레이아웃에 영향을 주는 회전이 필요할 때
  • FractionalTranslation: 크기 비율 기반 이동이 필요할 때
  • AnimatedRotation: 회전 애니메이션이 필요할 때
  • AnimatedScale: 크기 조절 애니메이션이 필요할 때