Animações de propriedades são efeitos visuais contínuos que ocorrem na interafce do usuário quando os valores dos parâmetros de propriedades animáveis mudam. Quando os valores mudam continuamente e são aplicados a interfaces de propriedades que podem causar mudanças na UI, é possível implementar animações de propriedades.
O ArkUI fornece o decorator @AnimatableExtend para definir interfaces de propriedades animáveis personalizadas. Como o tipo de dados dos parâmetros deve ter uma certa continuidade, os tipos de parâmetros suportados para interfaces de propriedades animáveis personalizadas são apenas o tipo number e tipos personalizados que implementam a interface AnimatableArithmetic<T>. Através de interfaces de propriedades animáveis personalizadas e tipos de dados animáveis, ao usar animateTo ou animation, modificando os valores de interfaces de propriedades não animáveis através de funções de callback por quadro, é possível fazer com que interfaces de propriedades não animáveis alcancem efeitos de animação. Também é possível modificar os valores de propriedades animáveis por quadro através de funções de callback por quadro para alcançar efeitos de layout quadro a quadro.
Usando tipo de dados number e decorator @AnimatableExtend para alterar a largura do componente Text e alcançar efeito de layout quadro a quadro
// Primeiro passo: usar o decorator @AnimatableExtend para definir interface de propriedade animável personalizada
@AnimatableExtend(Text)
function larguraAnimavel(largura: number) {
.width(largura)
}
@Entry
@Component
struct ExemploPropriedadeAnimavel {
@State larguraTexto: number = 80;
build() {
Column() {
Text("PropriedadeAnimavel")
.larguraAnimavel(this.larguraTexto)
.animation({ duration: 2000, curve: Curve.Ease })
Button("Reproduzir")
.onClick(() => {
this.larguraTexto = this.larguraTexto == 80 ? 160 : 80;
})
}.width("100%")
.padding(10)
}
}
Usando tipo de dados personalizado e decoratro @AnimatableExtend para alterar forma图形
declare type Coordenada = number[];
// Definir tipo de parâmetro para interface de propriedade animável, implementando funções de adição, subtração, multiplicação e comparação de igualdade da interface AnimatableArithmetic<t>
class Ponto extends Array<number> {
constructor(valor: Coordenada) {
super(valor[0], valor[1])
}
adicionar(direita: Ponto): Ponto {
let resultado: Coordenada = new Array<number>() as Coordenada;
for (let i = 0; i < 2; i++) {
resultado.push(direita[i] + this[i])
}
return new Ponto(resultado);
}
subtrair(direita: Ponto): Ponto {
let resultado: Coordenada = new Array<number>() as Coordenada;
for (let i = 0; i < 2; i++) {
resultado.push(this[i] - direita[i]);
}
return new Ponto(resultado);
}
multiplicar(escala: number): Ponto {
let resultado: Coordenada = new Array<number>() as Coordenada;
for (let i = 0; i < 2; i++) {
resultado.push(this[i] * escala)
}
return new Ponto(resultado);
}
}
// Definir tipo de parâmetro para interface de propriedade animável, implementando as funções da interface AnimatableArithmetic<t>
// O template T suporta tipos aninhados que implementam AnimatableArithmetic<t>
class VetorPontos extends Array<ponto> implements AnimatableArithmetic<array>> {
constructor(valorInicial: Array<coordenada>) {
super();
if (valorInicial.length) {
valorInicial.forEach((p: Coordenada) => this.push(new Ponto(p)))
}
}
// implementar a interface IAnimatableArithmetic
mais(direita: VetorPontos): VetorPontos {
let resultado = new VetorPontos([]);
const tamanho = Math.min(this.length, direita.length)
for (let i = 0; i < tamanho; i++) {
resultado.push(this[i].adicionar(direita[i]))
}
return resultado;
}
menos(direita: VetorPontos): VetorPontos {
let resultado = new VetorPontos([]);
const tamanho = Math.min(this.length, direita.length)
for (let i = 0; i < tamanho; i++) {
resultado.push(this[i].subtrair(direita[i]))
}
return resultado;
}
multiplicar(escala: number): VetorPontos {
let resultado = new VetorPontos([]);
for (let i = 0; i < this.length; i++) {
resultado.push(this[i].multiplicar(escala))
}
return resultado;
}
ehIgual(direita: VetorPontos): boolean {
if (this.length !== direita.length) {
return false;
}
for (let indice = 0, tamanho = this.length; indice < tamanho; ++indice) {
if (this[indice][0] !== direita[indice][0] || this[indice][1] !== direita[indice][1]) {
return false;
}
}
return true;
}
}
// Interface de propriedade animável personalizada
@AnimatableExtend(Polyline)
function pontosAnimaveis(pontos: VetorPontos) {
.points(pontos)
}
@Entry
@Component
struct FormaAnimada {
pontoInicialX: number = 75;
pontoInicialY: number = 25;
larguraQuadrado: number = 150;
translacaoFinalX: number = 50;
translacaoFinalY: number = 50;
@State vetorPontos1: VetorPontos = new VetorPontos([
[this.pontoInicialX, this.pontoInicialY],
[this.pontoInicialX + this.larguraQuadrado, this.pontoInicialY],
[this.pontoInicialX + this.larguraQuadrado, this.pontoInicialY + this.larguraQuadrado],
[this.pontoInicialX, this.pontoInicialY + this.larguraQuadrado]
]);
@State vetorPontos2: VetorPontos = new VetorPontos([
[this.pontoInicialX + this.translacaoFinalX, this.pontoInicialY + this.pontoInicialY],
[this.pontoInicialX + this.larguraQuadrado + this.translacaoFinalX, this.pontoInicialY + this.pontoInicialY],
[this.pontoInicialX + this.larguraQuadrado, this.pontoInicialY + this.larguraQuadrado],
[this.pontoInicialX, this.pontoInicialY + this.larguraQuadrado]
]);
@State cor: Color = Color.Green;
@State tamanhoFonte: number = 20.0;
@State vetorLinha1: VetorPontos = this.vetorPontos1;
@State vetorLinha2: VetorPontos = this.vetorPontos2;
build() {
Row() {
Polyline()
.width(300)
.height(200)
.backgroundColor("#0C000000")
.fill('#317AF7')
.pontosAnimaveis(this.vetorLinha1)
.animation({ duration: 2000, delay: 0, curve: Curve.Ease })
.onClick(() => {
if (this.vetorLinha1.ehIgual(this.vetorPontos1)) {
this.vetorLinha1 = this.vetorPontos2;
} else {
this.vetorLinha1 = this.vetorPontos1;
}
})
}
.width('100%').height('100%').justifyContent(FlexAlign.Center)
}
}
</coordenada></array></ponto></t></t></number></number></number></number></t>