Práctica de aplicación del efecto de movimiento de barrido de luz en terminal móvil

inserte la descripción de la imagen aquí

Autor | Siete

guía

Con el rápido desarrollo de Internet móvil, ha surgido en la industria una gran cantidad de experiencias interactivas creativas e interesantes. El efecto de movimiento de barrido es uno de los efectos de movimiento de carga interesantes. Los efectos de movimiento de barrido comunes incluyen el barrido de la pantalla esquelética y el barrido del logotipo. Entonces, ¿cuáles son los principios de estos dos efectos de barrido, cómo lograr estos dos efectos de barrido y cuáles son las diferencias entre las implementaciones de doble extremo de iOS y Andoird? Este artículo lo revelará en detalle.

El texto completo tiene 10.549 palabras y el tiempo de lectura previsto es de 27 minutos.

01 Introducción

Como un efecto de animación de carga común en el terminal móvil, el efecto de animación de barrido puede brindar a las personas una mejor experiencia visual y sensorial que la animación de carga giratoria tradicional. Su característica principal es que el efecto de la luz se extenderá con el tiempo, y el texto o patrón tiene la sensación de estar lleno de color.

El autor ha realizado el barrido de pantalla de esqueleto y la carga de barrido de pata de oso sucesivamente. Este artículo presentará la realización de estos dos efectos dinámicos de barrido y las diferencias técnicas entre los dos extremos desde las perspectivas de iOS y Android respectivamente.

imagen

△ Efecto de movimiento de barrido de luz de pata de oso

02 Efecto de barrido de pantalla Skeleton

La pantalla de esqueleto es un efecto de transición durante la carga de la interfaz. Antes de que se carguen los datos de la página, primero muestra al usuario la estructura general de la página y, después de obtener los datos de la interfaz, presenta el contenido real de la página y luego lo reemplaza. Esta tecnología puede reducir la ansiedad de los usuarios, hacer que el proceso de carga de la interfaz sea natural y fluido, y mejorar la experiencia del usuario. A menudo se utiliza para páginas de listas relativamente regulares, como listas de artículos y páginas de listas dinámicas. Aquí tomamos el panel de media pantalla de pago como ejemplo, y puede ver el efecto de los efectos de luz que se extienden por el diagrama esquelético.

[Falló la transferencia de la imagen del enlace externo, el sitio de origen puede tener un mecanismo de enlace antirrobo, se recomienda guardar la imagen y cargarla directamente (img-lyHmjYNh-1688436549789) (https://oscimg.oschina.net/oscnet/ up-36f4e302d644643978799d98f2abbe5e10b.gif)]

△Luz de escaneo de pantalla de esqueleto

2.1 Análisis del principio de barrido de pantalla de esqueleto

La escena de barrido de luz de la pantalla del esqueleto es relativamente simple, porque su fondo es opaco, y el efecto de barrido de luz se puede lograr superponiendo una vista de máscara en la imagen del esqueleto como un bloque de luz y moviendo la máscara.

La jerarquía de vistas se divide en dos capas como un todo, la capa inferior es la parte del esqueleto de la vista personalizada y la capa superior es la máscara transparente de degradado. Entre ellos, la parte del diagrama de esqueleto se implementa mediante una lista convencional, que no se repetirá aquí, y la máscara transparente degradada se usa como una luz de barrido, que se puede realizar cortando el diagrama o mediante código. En comparación con la implementación del código, la imagen de corte de máscara aumentará una parte del volumen del paquete, por lo que puede elegir personalizar una vista con el mismo tamaño que la imagen del esqueleto, superponerla en la imagen del esqueleto y configurarla para que sea gradualmente transparente. de izquierda a derecha.

Además, la animación de desplazamiento se puede realizar moviendo la vista de máscara desde el lado izquierdo del diagrama de esqueleto al lado derecho del diagrama de esqueleto en la dirección horizontal dentro del tiempo xxx.

2.2 Implementación de iOS

Core Animation es el soporte subyacente perfecto para AppKit y UIKit, y también está integrado en el flujo de trabajo de Cocoa y Cocoa Touch. Es el marco más básico para la representación y construcción de la interfaz de la aplicación. Las principales responsabilidades de Core Animation incluyen: renderizar, construir e implementar animaciones, y combinar diferentes contenidos visuales en la pantalla lo más rápido posible.Este contenido se descompone en capas independientes (específicamente CALayer en iOS) y se almacena como una jerarquía de forma de árbol. Este árbol también forma la base para UIKit y todo lo que vemos en la pantalla en una aplicación de iOS.

imagen

En iOS, se puede realizar a través del método de animación CALayer +View +Transform. capa es la capa subyacente de UIView, que es responsable del dibujo, la animación, el borde, la sombra y otros efectos visuales de la vista. La parte de la animación puede usar directamente el método de clase animateWithDuration of View, y el desplazamiento horizontal se puede realizar configurando la propiedad Transform de la vista en la devolución de llamada de la animación.

La parte de la máscara de degradado se puede definir de acuerdo con el siguiente código, utilizando ImageView como vista de máscara y configurando CAGradientLayer como su capa para lograr un efecto de degradado transparente:

// 创建自定义视图作为遮罩视图
_lightCover = [[UIImageView alloc] initWithFrame:self.bounds];
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = _lightCover.bounds;
// 渐变色颜色数组
gradientLayer.colors = [NSArray arrayWithObjects:
                    (id)[UIColorFromRGBA(0xFFFFFF, 0) CGColor], 
                    (id)[UIColorFromRGBA(0xFFFFFF, 0.3) CGColor], 
                    (id)[UIColorFromRGBA(0xFFFFFF, 0.5) CGColor], 
                    (id)[UIColorFromRGBA(0xFFFFFF, 0.3) CGColor], 
                    (id)[UIColorFromRGBA(0xFFFFFF, 0) CGColor], nil];
// 渐变的开始点 (不同的起始点可以实现不同位置的渐变,如图)
gradientLayer.startPoint = CGPointMake(0, 0.5f);
// 渐变的结束点
gradientLayer.endPoint = CGPointMake(1, 0.5f);
// 把渐变图层添加到遮罩视图的顶层
[_lightCover.layer insertSublayer:gradientLayer atIndex:0];
// 设置初始位置
_lightCover.transform = CGAffineTransformMakeTranslation(-self.bounds.size.width, 0);

Animación de desplazamiento de bucle mediante temporizador:

// 定时器:动画时间duration + 延迟时间delay = 定时器间隔时间intervalTime
self.lightSweepTimer = [NSTimer scheduledTimerWithTimeInterval:intervalTime target:self selector:@selector(lightSweepAnimation) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.lightSweepTimer forMode:NSRunLoopCommonModes];

La parte de animación se implementa directamente con el método animateWithDuration de View:

self.lightCover.transform = CGAffineTransformMakeTranslation(-self.bounds.size.width, 0);
[UIView animateWithDuration:duration animations:^{
    self.lightCover.transform = CGAffineTransformMakeTranslation(self.bounds.size.width, 0.f);
} completion:^(BOOL finished) {

}];

Debido a que el tiempo de ejecución del temporizador es largo, puede realizar una animación primero al cargar:

//定时器时间较长,先执行一次动画
[self lightSweepAnimation];

2.3 Implementación de Android

La tecnología de renderizado de Android se basa principalmente en el sistema View, que maneja el diseño y el dibujo de las vistas. View representa un control, que es principalmente responsable de su propio dibujo, y ViewGroup representa un contenedor, que es principalmente responsable de administrar y diseñar las subvistas y los subgrupos de vista que contiene.

imagen

Esto se puede lograr personalizando Shape+ObjectAnimator. Shape es un tipo especial de Vista, que realiza formas personalizadas y efectos relacionados a través de etiquetas definidas en XML. Se pueden dibujar varias formas a través de atributos relacionados, y se les pueden aplicar degradados, sombras, bordes y otros efectos. ObjectAnimator se puede usar para todas las propiedades que admiten animación, incluida la posición, el tamaño, la rotación, la escala y la transparencia, etc., solo especifique el nombre de la propiedad y el valor de destino que desea animar.

La parte de la máscara puede definir un rectángulo degradado de la siguiente manera:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:startColor="#00ffffff"
        android:centerColor="#7fffffff"
        android:endColor="#00ffffff"
        ></gradient>
</shape>

imagen

Defina otro controlador para actualizar la vista en el hilo principal:

Handler mHandler = new Handler(Looper.getMainLooper(), new Handler.Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        int what = msg.what;
        if (msgAnimation == what) {
            runAnimation();
        }
        return false;
    }
});

Defina la animación de desplazamiento a través de ObjectAnimator y atributo translationX:

private void runAnimation() {
    if (displayWidth == 0) {
        displayWidth = defaultWidth;
    }
    ObjectAnimator translationX = ObjectAnimator.ofFloat(mMoveLight, "translationX", -displayWidth, displayWidth);
    translationX.setDuration(duration);
    translationX.setRepeatCount(0);
    translationX.start();
    translationX.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            sendMsg(msgAnimation, delayTime);
        }
    });
}

Continuar la animación después de un retraso:

private void sendMsg(int what, int delayTime) {
    checkParent();
    if (mHandler != null) {
        mHandler.sendEmptyMessageDelayed(what, delayTime);
    }
}

Cuando comience la carga, realice primero una animación:

public void startLoading() {
    setVisibility(VISIBLE);
    sendMsg(msgAnimation, 0);
}

03 efecto de movimiento de barrido de luz de pata de oso

La luz de barrido de pata de oso se utiliza principalmente como un efecto de transición cuando se carga la página, y se mostrará antes de que se cargue el contenido. Por lo general, se encuentra en la parte superior del contenido de la página y no puede cubrir completamente el contenido inferior. Y en el modo diurno (con una variedad de colores de fondo de contenido), modo nocturno (sobre la base del modo diurno, cubierto con una capa de máscara gris transparente), modo oscuro y varios modos de escena, también afectará el barrido. el efecto es perturbador, especialmente en el fondo gris del modo diurno y en el modo nocturno, es posible que la luz de barrido ni siquiera sea visible.

imagen

△ Fondo blanco para el modo diurno, fondo gris para el modo diurno, modo nocturno y modo oscuro

3.1 Implementación de iOS

En la compleja escena del barrido de la pata de oso, la superposición de vistas de doble capa por sí sola no puede satisfacer las necesidades, y el control deslizante tendrá varias situaciones anormales (consulte la sección 3.2.1 para obtener más detalles). Se puede usar una estructura de tres capas en iOS. La capa inferior es la imagen que se escaneará, la del medio es el bloque de luz en movimiento y la capa superior es la capa hueca dibujada de acuerdo con la imagen base. Las tres capas de vistas son superpuestos para formar una mezcla de luz y patrones.Efecto.

imagen

△iOS se da cuenta del principio del efecto de barrido a través de la máscara

El marco CoreAnimation de iOS es excelente, y su implementación View solo satisface las necesidades de esta estructura de tres niveles.

imagen

  • ver ver

  • La vista es un elemento básico de la interfaz de usuario que se utiliza para mostrar y procesar la interfaz de usuario. Pueden ser controles de interfaz de usuario estándar (como UILabel, UIButton, etc.) o vistas personalizadas.

  • Cada Vista tiene su propia área de dibujo y puede contener otras Vistas como sus subvistas.

  • capa capa

  • La capa es una parte integral de la jerarquía de dibujo subyacente de View. Cada vista tiene un objeto de capa (una instancia de la clase CALayer) asociado.

  • La capa es responsable de manejar el dibujo y la visualización del contenido de la Vista, incluido el color de fondo, el borde, la sombra, etc. de la Vista. Cada Capa tiene su propia área de dibujo, que corresponde a los límites de la Vista.

  • máscara máscara

  • La máscara es un mecanismo para controlar la visibilidad de las capas. Es una imagen o forma transparente que se puede asociar con una Capa.

  • Al aplicar una máscara, es posible definir qué áreas de la Capa deben ser visibles y cuáles deben ocultarse.

  • Las máscaras generalmente se crean a partir de otra capa o una imagen personalizada y determinan la parte visible del contenido de la capa.

iOS puede usar la capa como luz y la máscara como máscara para lograr el efecto de mezcla de luces en el logotipo de la pata de oso:

// loadingView 设置熊掌底图
self.loadingImgView.image = [self lightImg];

// 创建渐变图层
CAGradientLayer *gradientLayer = [CAGradientLayer layer];
gradientLayer.frame = CGRectMake(0, 0, loadingWidth, loadingHeight);
// 设置渐变颜色
gradientLayer.colors = @[(__bridge id)[UIColor colorWithWhite:1 alpha:0].CGColor, 
                        (__bridge id)[UIColor colorWithWhite:1 alpha:0.9].CGColor, 
                        (__bridge id)[UIColor colorWithWhite:1 alpha:0.9].CGColor, 
                        (__bridge id)[UIColor colorWithWhite:1 alpha:0].CGColor];
gradientLayer.locations = @[@0.0, @0.49, @0.495, @1.0];
gradientLayer.startPoint = CGPointMake(0, 0.5);
gradientLayer.endPoint = CGPointMake(1, 0.35);
[self.loadingImgView.layer addSublayer:gradientLayer];

// 创建透明遮罩
CALayer *maskLayer = [[CALayer alloc] init];
maskLayer.frame = CGRectMake(0, 0, loadingWidth, loadingHeight);
maskLayer.backgroundColor = [UIColor clearColor].CGColor;
// 设置遮罩内容
maskLayer.contents = (__bridge id _Nullable)([self lightImg].CGImage);
self.loadingImgView.layer.mask = maskLayer;

Realice el desplazamiento horizontal a través de CABasicAnimation en la capa de capa:

// 定义基本动画, 控制在x轴方向的位移
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.translation.x"];
animation.duration = 2;
// 重复次数1000000次(无限次)
animation.repeatCount = 1000000;
// 动画不会自动反转
animation.autoreverses = false;
animation.fromValue = @(-loadingWidth);
animation.toValue = @(loadingWidth);
// 动画在完成后不会被移除
animation.removedOnCompletion = NO;
// 动画结束后图层保持最后一个状态
animation.fillMode = kCAFillModeForwards;
[self.gradientLayer addAnimation:animation forKey:@"loading_animation_key"];

3.2 Implementación de Android

Aquí tomamos como ejemplo la superposición de vista de doble capa en Android, y podemos ver que habrá varios problemas.

3.2.1 Superposición mediante vista personalizada de doble capa

Realizado mediante la superposición de imágenes cortadas, los problemas existentes son: el control deslizante no se puede ver en el modo día (fondo gris) y el control deslizante es más evidente en el modo oscuro.

imagen

△ Corte superpuesto

Realizado superponiendo Shape, el efecto de barrido en la escena de la pantalla del esqueleto está bien, pero el efecto en el barrido de la pata del oso no es bueno. Los problemas existentes son: si el fondo blanco es normal en el modo diurno, pero el fondo gris no puede ver el control deslizante. Además, sin agregar el ángulo de rotación, el efecto en el modo oscuro no es malo. Después de superponer el ángulo de rotación, puede ver los rastros obvios del control deslizante.

imagen

△Capa de forma superpuesta

imagen

△ Capa de forma superpuesta (ángulo de rotación)

Incluso puede implementar un barrido de gradiente personalizado con una pendiente a través de LinearGradient. El método principal es el siguiente:

// float k = 1f * h / w;
mValueAnimator = ValueAnimator.ofFloat(0f - offset * 2, w + offset * 2);
mValueAnimator.setRepeatCount(repeatCount);
mValueAnimator.setInterpolator(new LinearInterpolator());
mValueAnimator.setDuration(duration);
mValueAnimator.addUpdateListener(animation -> {
    float value = (float) animation.getAnimatedValue();
    LinearGradient mLinearGradient = new LinearGradient(
            value,
            k * value,
            value + offset,
            k * (value + offset),
            colors,
            positions,
            Shader.TileMode.CLAMP
    );
    mPaint.setShader(mLinearGradient);
    invalidate();
});
mValueAnimator.start();

En la devolución de llamada de actualización de ValueAnimator:

  • Calcular las coordenadas de dos puntos de control según el valor y la pendiente k

  • Cree un degradado lineal LinearGradient, definido por estos dos puntos de control

  • Establecer el degradado como Shader de Paint

  • Llame a invalidate () para volver a dibujar la Vista

Dado que ValueAnimator se actualiza constantemente, los dos puntos de control del degradado lineal también cambian constantemente, lo que da como resultado un efecto de animación de degradado:

imagen

Pero de esta manera, el efecto es mejor solo en el fondo blanco, y no es satisfactorio en el fondo gris o el efecto oscuro, y puedes ver el control deslizante más obvio.

3.2.2 Dibujar a través de Canvas

A diferencia de iOS en Android, la vista en sí puede configurar varias capas. Si desea renderizar la luz de barrido y la imagen de fondo de forma mixta, a menos que personalice una capa de máscara para formar una estructura de tres capas, o la manipule directamente a través del dibujo de nivel inferior.

Entonces, ¿cómo mezclar y renderizar la luz de barrido y la imagen de fondo? La respuesta es que se puede lograr a través de PorterDuffXferMode.

PorterDuffXferMode usa las reglas de PorterDuff.Mode para mezclar los gráficos dibujados con los gráficos en el lienzo y finalmente actualiza el lienzo para mostrar nuevos gráficos. El uso de PorterDuffXferMode también es muy simple. Cuando necesite usarlo, paint.setXfermode (modo PorterDuff.Mode) establece el modo mixto.

PorterDuff.Mode se divide en 16 modos: CLEAR, SRC, DST, SRC_OVER, DST_OVER, SRC_IN, DST_IN, SRC_OUT, DST_OUT, SRC_ATOP, DST_ATOP, XOR, DARKEN, LIGHTEN, MULTIPLY, SCREEN.

Android usa Canvas para dibujar gráficos en View. Los píxeles de los gráficos dibujados se denominan píxeles de origen (fuente, src para abreviar) y los píxeles del rectángulo dibujado en la posición correspondiente en Canvas se denominan píxeles de destino (destino, dst para abreviar). ). ). Los cuatro componentes ARGB del píxel de origen y los cuatro componentes ARGB del píxel de destino en la misma posición en el lienzo se calculan de acuerdo con las reglas definidas por Xfermode para formar el valor ARGB final, y luego el valor ARGB final se usa para actualizar el valor ARGB del píxel de destino.

Para ilustrar con la ilustración provista en el sitio web oficial, supongamos que hay un gráfico de píxel de origen azul y un gráfico de píxel de destino rojo.

imagen

A través de DST_IN, puede obtener el sector cuya parte de intersección es roja, es decir, la parte de intersección conserva el píxel de destino y la parte que no intersecta descarta el píxel de origen.

imagen

De esta manera, primero configure el efecto de mezcla de DST_IN a través de PorterDuffXfermode y cree el efecto de degradado de la máscara a través de LinearGradient. En segundo lugar, use Canvas y Paint para dibujar y renderizar mapas de bits, primero dibuje un mapa de bits sin máscara como src, luego dibuje un mapa de bits enmascarado como dst y combine los dos para formar un efecto de barrido y finalmente realice el efecto de animación usando ValueAnimator.

imagen

△ La parte brillante de la figura es dst

Primero, cree un mapa de bits de máscara de degradado con una pendiente:

mMaskBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(mMaskBitmap);
// 可以通过参数控制getGradientColors 的值,在不同模式下为不同的渐变颜色
Shader gradient = new LinearGradient(
                        0, 0,
                        width, 0,
                        getGradientColors(),
                        getGradientPositions(),
                        Shader.TileMode.REPEAT);
canvas.rotate(mTilt, width / 2, height / 2);
Paint paint = new Paint();
paint.setShader(gradient);
// 适度增大矩形区域,适配倾斜
int padding = (int) (Math.sqrt(2) * Math.max(width, height)) / 2;
canvas.drawRect(-padding, -padding, width + padding, height + padding, paint);

En segundo lugar, dibuje el mapa de bits de origen y el mapa de bits de destino en el método dispatchDraw:

// 先绘制一个未经过遮罩处理的位图,作为 src
drawUnmasked(new Canvas(unmaskBitmap));

Canvas unmaskRenderCanvas = (new Canvas(maskBitmap));
unmaskRenderCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
super.dispatchDraw(unmaskRenderCanvas);
canvas.drawBitmap(unmaskBitmap, 0, 0, mAlphaPaint);

// 再绘制一个经过遮罩处理的位图,作为dst
Canvas maskRenderCanvas = (new Canvas(maskBitmap));
maskRenderCanvas.clipRect(
    mMaskOffsetX,
    mMaskOffsetY,
    mMaskOffsetX + maskBitmap.getWidth(),
    mMaskOffsetY + maskBitmap.getHeight());
maskRenderCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
super.dispatchDraw(maskRenderCanvas);

maskRenderCanvas.drawBitmap(maskBitmap, mMaskOffsetX, mMaskOffsetY, mMaskPaint);
canvas.drawBitmap(maskBitmap, 0, 0, null);

Luego, realice el desplazamiento y active el efecto de flash de dibujo en tiempo real a través de ValueAnimator:

mMaskTranslation.set(-width, 0, width, 0);
mAnimator = ValueAnimator.ofFloat(0.0f, 1.0f + (float) mRepeatDelay / mDuration);
mAnimator.setDuration(mDuration + mRepeatDelay);
mAnimator.setRepeatCount(mRepeatCount);
mAnimator.setRepeatMode(mRepeatMode);
mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
    float value = Math.max(0.0f, Math.min(1.0f, (Float) animation.getAnimatedValue()));
    mMaskOffsetX = (int) (mMaskTranslation.fromX * (1 - value) + mMaskTranslation.toX * value);
    mMaskOffsetY = (int) (mMaskTranslation.fromY * (1 - value) + mMaskTranslation.toY * value);
    invalidate();
}
});

El efecto final se muestra en la siguiente figura:

imagen

△Modo día

imagen

△Modo nocturno

imagen

△Modo oscuro

04 Epílogo

En el contenido anterior, introdujimos el efecto de barrido basado en la máscara. Las aplicaciones comunes de la máscara son el efecto de esquina redondeada, el aluvión de retrato penetrante, y se usa para dibujar el efecto de excavación de agujeros en la guía de principiantes, o el Efecto rasca billete de lotería. .

La tecnología de renderizado se aplica principalmente al marco Core Animation en el sistema iOS y al sistema View de Android.

Core Animation generalmente se usa en iOS para implementar la animación de manera eficiente y conveniente. Utiliza CALayer para operaciones de renderizado y animación de gráficos. Apple no proporciona soporte de enmascaramiento directamente en UIView, pero lo implementa en su CALayer subyacente. Esto permite a los desarrolladores controlar y modificar la máscara de manera flexible para lograr efectos más potentes. Y Android quiere crear efectos más flexibles y potentes, que se pueden lograr a través de Canvas.

--FIN--

Lectura recomendada:

Análisis y problemas de fortalecimiento de la seguridad de Android SDK

Práctica cuantitativa a gran escala del modelo semántico de búsqueda.

Cómo diseñar una plataforma eficiente de servicios de registros distribuidos

Modelo de coincidencia semántica multimodal en recuperación de imágenes y videos: principios, implicaciones, aplicaciones y perspectivas

Gestión de recursos sin conexión de Baidu

Baidu APP iOS terminal paquete tamaño 50M práctica de optimización (3) optimización de recursos

Supongo que te gusta

Origin blog.csdn.net/lihui49/article/details/131529241
Recomendado
Clasificación