Reducción del uso de la CPU

Otro aspecto importante en la optimización es el uso de la CPU. La optimización del procesamiento de CPU mejora el rendimiento y, como resultado, la duración de la batería en dispositivos móviles.

Mejoras de Flash Player 10.1 en el uso de la CPU

Flash Player 10.1 introduce dos nuevas funciones que ayudan a ahorrar procesamiento de CPU. Estas funciones están relacionadas con la pausa y la reanudación de contenido SWF cuando sale de la pantalla y limitar el número de instancias de Flash Player en una página.

Pausa, aceleración y reanudación

Nota: la función de pausa, aceleración y reanudación no se aplica a las aplicaciones de Adobe® AIR®.

Para optimizar el uso de la batería y la CPU, Flash Player 10.1 introduce una nueva función relacionada con las instancias inactivas. El objetivo de esta función consiste en limitar el uso de la CPU deteniendo y reanudando el contenido SWF cuando éste se encuentra fuera y dentro de pantalla. Con esta función, Flash Player libera toda la memoria posible eliminando cualquier objeto que se pueda volver al crear cuando se reanude la reproducción del contenido. El contenido se considera fuera de pantalla cuando todo el contenido se encuentra de este modo.

Existen dos escenarios que hacen que el contenido SWF esté fuera de pantalla:

  • El usuario desplaza la página y hace que el contenido SWF se mueva fuera de pantalla.

    En este caso, si existe alguna reproducción de audio o vídeo, el contenido se sigue reproduciendo, pero la representación se detiene. Si no hay reproducción de vídeo ni audio, para garantizar que no se detenga la reproducción o la ejecución de ActionScript, establezca el parámetro HTML hasPriority en true. Sin embargo, la representación del contenido SWF se detiene cuando éste se encuentra oculto o fuera de pantalla, independientemente de valor del parámetro HTML hasPriority .

  • Una ficha está abierta en el navegador, lo que causa que el contenido SWF se desplace hacia el fondo.

    En este caso, independientemente del valor de la etiqueta HTML hasPriority , el contenido SWF se ralentiza o se acelera entre 2 y 8 fps. La reproducción de audio y vídeo se detiene y no se representa ningún contenido a no ser que el contenido SWF se haga visible de nuevo.

Para Flash Player 11.2 y versiones posteriores ejecutadas en navegadores de escritorio de Windows y Mac, es posible usar el evento ThrottleEvent en la aplicación. Flash Player distribuye un evento ThrottleEvent cuando Flash Player se pone en pausa, acelera o reanuda la reproducción.

El evento ThrottleEvent es un evento de difusión, lo que significa que lo distribuyen todos los objetos EventDispatcher con un detector registrado para este evento. Para obtener más información sobre los eventos de difusión, consulte la clase DisplayObject .

Administración de instancias

Nota: la función de gestión de instancias no se aplica a las aplicaciones de Adobe® AIR®.
Utilice el parámetro HTML hasPriority para retrasar la carga de los archivos SWF fuera de pantalla.

Flash Player 10.1 introduce un nuevo parámetro HTML denominado hasPriority :

<param name="hasPriority" value="true" />

Esta función limita el número de instancias de Flash Player que se inician en una página. La limitación del número de instancias ayuda a conservar recursos de batería y CPU. La idea consiste en asignar una prioridad específica al contenido SWF, proporcionando cierta prioridad de contenido sobre los demás contenidos de una página. Pongamos un sencillo ejemplo: un usuario está navegando en un sitio web y la página de índice aloja tres archivos SWF diferentes. Uno de ellos puede verse, otro presenta visibilidad parcial en pantalla y el último está fuera de pantalla y requiere un desplazamiento. Las primeras dos animaciones se inician con normalidad, pero la última queda aplazada hasta que sea visible. Este escenario es el comportamiento predeterminado cuando el parámetro hasPriority no está presente o se establece en false . Para garantizar que un archivo SWF se haya iniciado, aunque esté fuera de pantalla, establezca el parámetro hasPriority en true . No obstante, independientemente del valor del parámetro hasPriority , un archivo SWF que no esté visible para el usuario siempre tiene detenido su procesamiento.

Nota: si los recursos de CPU disponibles son escasos, las instancias de Flash Player no vuelven a iniciarse automáticamente, aunque el parámetro hasPriority se establezca en true . Si las nuevas instancias se crean mediante JavaScript una vez que se haya cargado la página, estas instancias omitirán el indicador hasPriority . Se iniciará cualquier contenido de 1x1 ó 0x0 píxeles, evitando que los archivos SWF auxiliares se aplacen si el administrador de web no consigue incluir el indicador hasPriority . Sin embargo, los archivos SWF se pueden iniciar cuando se hace clic en los mismos. Este comportamiento se denomina “clic para reproducción”.

Los siguientes diagramas muestran los efectos del establecimiento del parámetro hasPriority en diferentes valores:

Efectos de diferentes valores para el parámetro hasPriority.

Efectos de diferentes valores para el parámetro hasPriority.

Modo de suspensión

Flash Player 10.1y AIR 2.5 introducen una nueva función en los dispositivos móviles que ayuda a ahorrar procesamiento de CPU y, como resultado, duración de la batería. Esta función implica la retroiluminación que se encuentra en diversos dispositivos móviles. Por ejemplo, si un usuario que ejecuta una aplicación móvil tiene una interrupción y deja de utilizar el dispositivo, el motor de ejecución detecta el momento en que la retroiluminación entra en modo de suspensión. Posteriormente, reduce la velocidad de fotogramas a 4 fotogramas por segundo (fps) y detiene la representación. En el caso de aplicaciones de AIR, el modo de suspensión también comienza cuando la aplicación pasa a segundo plano.

El código ActionScript continúa ejecutándose en modo de suspensión, lo que es similar al establecimiento de la propiedad Stage.frameRate en 4 fps. Sin embargo, el paso de representación se omite, por lo que el usuario no puede ver que el reproductor se está ejecutando a 4 fps. Se ha seleccionado una velocidad de fotogramas de 4 fps, en lugar de cero, ya que permite que todas las conexiones permanezcan abiertas (NetStream, Socket y NetConnection). El cambio a cero podría interrumpir las conexiones abiertas. Se ha elegido una velocidad de actualización de 250 ms (4 fps), ya que muchos fabricantes de dispositivos utilizan este valor como frecuencia de actualización. Con este valor, la velocidad de fotogramas del motor de ejecución se mantiene en el mismo alcance que el propio dispositivo.

Nota: cuando el motor de ejecución está en modo de suspensión, la propiedad Stage.frameRate devuelve la velocidad de fotogramas del archivo original SWF, en lugar de 4 fps.

Cuando la retroiluminación vuelve al modo activo, se reanuda la representación. La velocidad de fotogramas vuelve a su valor original. Pongamos como ejemplo un reproductor con el que un usuario está escuchando música. Si la pantalla entra en modo de suspensión, el motor de ejecución responde en función del tipo de contenido que se está reproduciendo. Existe una lista de situaciones con el correspondiente comportamiento del motor de ejecución:

  • La retroiluminación entra en modo de suspensión y se está reproduciendo contenido que no es A/V: la representación se detiene y la velocidad de fotogramas se establece en 4 fps.

  • La retroiluminación entra en modo de suspensión y se está reproduciendo contenido A/V: el motor de ejecución hace que la retroiluminación siempre esté activa y continúa la reproducción.

  • La retroiluminación pasa de modo de suspensión a modo activo: el motor de ejecución establece la velocidad de fotogramas en la configuración de velocidad de fotogramas del archivo SWF original y reanuda la representación.

  • Flash Player se detiene mientras que se reproduce el contenido A/V: Flash Player restablece el estado de retroiluminación al comportamiento del sistema predeterminado debido a que A/V ya no se está reproduciendo.

  • El dispositivo móvil recibe una llamada de teléfono durante la reproducción del contenido A/V: la representación se detiene y la velocidad de fotogramas se establece en 4 fps.

  • El modo de suspensión de la retroiluminación se desactiva en un dispositivo móvil: el motor de ejecución se comporta con normalidad.

Cuando la retroiluminación pasa a modo de suspensión, el procesamiento se detiene y la velocidad de fotogramas se ralentiza. Con esta función se ahorra procesamiento de CPU, pero no es posible basarse en la misma para crear una pausa real, tal y como sucede en una aplicación de juego.

Nota: no se distribuye ningún evento de ActionScript cuando el motor de ejecución entra o sale del modo de suspensión.

Bloqueo y desbloqueo de objetos

Bloquee y desbloquee los objetos de forma adecuada utilizando los eventos REMOVED_FROM_STAGE y ADDED_TO_STAGE .

Para optimizar el código, bloquee y desbloquee siempre los objetos. Los procesos de bloqueo y desbloqueo son importantes para todos los objetos, pero especialmente para los objetos de visualización. Aunque los objetos de visualización ya no estén en la lista de visualización y estén esperando a ser recogidos por el recolector de elementos no utilizados, aún pueden estar utilizando código con bastantes recursos de la CPU. Por ejemplo, aún pueden estar utilizando Event.ENTER_FRAME. Como resultado, es fundamental bloquear y desbloquear objetos correctamente con los eventos Event.REMOVED_FROM_STAGE y Event.ADDED_TO_STAGE . El siguiente ejemplo muestra la reproducción de un clip de película en el escenario que interactúa con el teclado:

// Listen to keyboard events 
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyIsDown); 
stage.addEventListener(KeyboardEvent.KEY_UP, keyIsUp); 
  
// Create object to store key states 
var keys:Dictionary = new Dictionary(true); 
  
function keyIsDown(e:KeyboardEvent):void 
{ 
    // Remember that the key was pressed 
    keys[e.keyCode] = true;     
  
    if (e.keyCode==Keyboard.LEFT || e.keyCode==Keyboard.RIGHT) 
    { 
        runningBoy.play(); 
    } 
} 
  
function keyIsUp(e:KeyboardEvent):void 
{ 
    // Remember that the key was released 
    keys[e.keyCode] = false; 
  
    for each (var value:Boolean in keys) 
          if ( value ) return; 
    runningBoy.stop(); 
} 
  
runningBoy.addEventListener(Event.ENTER_FRAME, handleMovement); 
runningBoy.stop(); 
  
var currentState:Number = runningBoy.scaleX; 
var speed:Number = 15; 
  
function handleMovement(e:Event):void 
{ 
    if (keys[Keyboard.RIGHT]) 
    { 
        e.currentTarget.x += speed; 
        e.currentTarget.scaleX = currentState;     
    } else if (keys[Keyboard.LEFT]) 
    { 
        e.currentTarget.x -= speed; 
        e.currentTarget.scaleX = -currentState; 
    } 
}

Ver gráfico a tamaño completo
Clip de película que interactúa con el teclado.

Cuando se hace clic en el botón Quitar, el clip de película se elimina de la lista de visualización:

// Show or remove running boy 
showBtn.addEventListener (MouseEvent.CLICK,showIt); 
removeBtn.addEventListener (MouseEvent.CLICK,removeIt); 
 
function showIt (e:MouseEvent):void 
{ 
    addChild (runningBoy); 
} 
 
function removeIt(e:MouseEvent):void 
{ 
    if (contains(runningBoy)) removeChild(runningBoy); 
}

Aunque se haya eliminado de la lista de visualización, el clip de película aún distribuye el evento Event.ENTER_FRAME . El clip de película aún se ejecuta, pero no se representa. Para administrar la situación correctamente, detecte los eventos adecuados y elimine los detectores de eventos con el fin de evitar la ejecución de código que implique una carga para los recursos de CPU:

// Listen to Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE 
runningBoy.addEventListener(Event.ADDED_TO_STAGE,activate); 
runningBoy.addEventListener(Event.REMOVED_FROM_STAGE,deactivate); 
 
function activate(e:Event):void 
{ 
    // Restart everything 
    e.currentTarget.addEventListener(Event.ENTER_FRAME,handleMovement); 
} 
 
function deactivate(e:Event):void 
{ 
    // Freeze the running boy - consumes fewer CPU resources when not shown 
    e.currentTarget.removeEventListener(Event.ENTER_FRAME,handleMovement); 
    e.currentTarget.stop(); 
}

Al presionar el botón Mostrar, el clip de película se reinicia, éste vuelve a detectar los eventos Event.ENTER_FRAME y el teclado controla correctamente el clip de película.

Nota: si algún objeto de visualización se elimina de la lista de visualización, el establecimiento de su referencia en null tras eliminarlo no garantiza el bloqueo del objeto. Si no se ejecuta el recolector de elementos no utilizados, el objeto continúa consumiendo memoria y procesamiento de CPU, aunque ya no se muestre. Para garantizar que el objeto consuma el mínimo procesamiento de CPU posible, asegúrese de bloquearlo por completo al eliminarlo de la lista de visualización.

Desde Flash Player 10 y AIR 1.5, también se produce el siguiente comportamiento. Si la cabeza lectora encuentra un fotograma vacío, el objeto de visualización se bloquea automáticamente aunque no se haya implementado ningún comportamiento de bloqueo.

El concepto de bloqueo también resulta importante al cargar contenido remoto con la clase Loader. Al utilizar la clase Loader con Flash Player 9 y AIR 1.0, era necesario bloquear el contenido manualmente detectando el evento Event.UNLOAD distribuido por el objeto LoaderInfo. Todos los objetos tenían que bloquearse manualmente, lo cual implicaba una tarea compleja. En Flash Player 10 y AIR 1.5 se introdujo un nuevo método importante en la clase Loader denominado unloadAndStop() . Este método permite descargar un archivo SWF, bloquear automáticamente todos los objetos en el archivo SWF cargado y provocar la ejecución del recolector de elementos no utilizados.

En el siguiente código, el archivo SWF se carga y después de descarga utilizando el método unload() , que requiere más procesamiento y bloqueo manual:

var loader:Loader = new Loader(); 
 
loader.load ( new URLRequest ( "content.swf" ) ); 
 
addChild ( loader ); 
 
stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
 
function unloadSWF ( e:MouseEvent ):void 
{ 
    // Unload the SWF file with no automatic object deactivation 
    // All deactivation must be processed manually 
    loader.unload(); 
}

Una buena práctica consiste en utilizar el método unloadAndStop() , que administra el bloqueo de forma nativa y provoca la ejecución del proceso de recolección de elementos no utilizados:

var loader:Loader = new Loader(); 
 
loader.load ( new URLRequest ( "content.swf" ) ); 
 
addChild ( loader ); 
 
stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
 
function unloadSWF ( e:MouseEvent ):void 
{ 
    // Unload the SWF file with automatic object deactivation 
    // All deactivation is handled automatically 
    loader.unloadAndStop(); 
}

Cuando se llama al método unloadAndStop() se producen las siguientes operaciones:

  • Se detienen los sonidos.

  • Los detectores registrados en la línea de tiempo principal del archivo SWF se eliminan.

  • Los objetos del temporizador se detienen.

  • Los dispositivos periféricos de hardware (como la cámara y el micrófono) se liberan.

  • Todos los clips de película se detienen.

  • La distribución Event.ENTER_FRAME , Event.FRAME_CONSTRUCTED , Event.EXIT_FRAME , Event.ACTIVATE y Event.DEACTIVATE se detiene.

Eventos activate y deactivate

Utilice los eventos Event.ACTIVATE y Event.DEACTIVATE para detectar inactividad en segundo plano y optimizar su aplicación según corresponda.

Hay dos eventos ( Event.ACTIVATE y Event.DEACTIVATE ) que pueden ayudarle a perfeccionar la aplicación para que use el menor número de ciclos de CPU posible. Estos eventos permiten detectar si el motor de ejecución tiene o deja de tener la selección. Como resultado, se puede optimizar el código para reaccione ante distintos cambios de contexto. El siguiente código detecta ambos eventos y cambia dinámicamente la velocidad de fotogramas a cero cuando la aplicación deja de estar seleccionada. Una animación puede perder la selección, por ejemplo, cuando el usuario cambia a otra pestaña o cuando minimiza la aplicación:

var originalFrameRate:uint = stage.frameRate; 
var standbyFrameRate:uint = 0; 
  
stage.addEventListener ( Event.ACTIVATE, onActivate ); 
stage.addEventListener ( Event.DEACTIVATE, onDeactivate ); 
  
function onActivate ( e:Event ):void 
{ 
    // restore original frame rate 
    stage.frameRate = originalFrameRate; 
} 
  
function onDeactivate ( e:Event ):void 
{ 
    // set frame rate to 0 
    stage.frameRate = standbyFrameRate; 
}

Cuando la aplicación vuelve a estar seleccionada, la velocidad de fotogramas se restablece en su valor original. En lugar de cambiar la velocidad de datos dinámicamente, también puede plantearse otras mejoras, como bloquear y liberar objetos.

Los eventos y deactivate permiten implementar un mecanismo similar al de la función “Pausa y reanudación” que algunos dispositivos móviles y netbooks ya incluyen de serie.

Interacciones de ratón

Considere la desactivación de interacciones de ratón, si es posible.

Cuando se utiliza un objeto interactivo como, por ejemplo, MovieClip o Sprite, el motor de ejecución ejecuta código nativo para detectar y administrar las interacciones de ratón. La detección de la interacción de ratón puede implicar una carga para los recursos de CPU cuando varios objetos interactivos se muestran en pantalla, especialmente si se superponen. Una forma sencilla de evitar este procesamiento consiste en deshabilitar las interacciones de ratón en objetos que no requieran ninguna interacción de este tipo. El siguiente código ilustra el uso de las propiedades mouseEnabled y mouseChildren :

// Disable any mouse interaction with this InteractiveObject 
myInteractiveObject.mouseEnabled = false; 
const MAX_NUM:int = 10; 
  
// Create a container for the InteractiveObjects 
var container:Sprite = new Sprite(); 
  
for ( var i:int = 0; i< MAX_NUM; i++ ) 
{ 
    // Add InteractiveObject to the container 
    container.addChild( new Sprite() ); 
} 
  
// Disable any mouse interaction on all the children 
container.mouseChildren = false;

Si es posible, considere la desactivación de la interacción de ratón, lo que ayuda a que la aplicación utilice menos procesamiento de CPU y, como resultado, reduce el uso de la batería en dispositivos móviles.

Temporizadores frente a eventos ENTER_FRAME

Seleccione temporizadores o eventos ENTER_FRAME , dependiendo de si el contenido es animado.

Los temporizadores son más recomendables que los eventos Event.ENTER_FRAME para el contenido no animado que se ejecuta durante mucho tiempo.

En ActionScript 3.0, existen dos maneras de llamar a una función en intervalos específicos. El primer enfoque consiste en utilizar el evento Event.ENTER_FRAME distribuido por objetos de visualización (DisplayObject). El segundo enfoque radica en utilizar un temporizador. Los desarrolladores de ActionScript suelen utilizar el enfoque del evento ENTER_FRAME . El evento ENTER_FRAME se distribuye en todos los fotogramas. Como resultado, el intervalo en el que se llama a la función se relaciona con la velocidad de fotogramas real actual. A la velocidad de fotogramas se accede mediante la propiedad Stage.frameRate . Sin embargo, en algunos casos, el uso de un termporizador puede ser una mejor opción que la utilización del evento ENTER_FRAME . Por ejemplo, si no se utiliza la animación pero se desea que el código se llame en intervalos específicos, el uso de un temporizador puede constituir una opción más adecuada.

Un temporizador se puede comportar del mismo modo que un evento ENTER_FRAME , pero un evento se puede distribuir sin tener que asociarse a la velocidad de fotogramas. Este comportamiento puede ofrecer cierta optimización importante. Considere una aplicación de reproductor de vídeo como ejemplo. En este caso, no es necesario utilizar una velocidad de fotogramas elevada, ya que únicamente se están moviendo los controles de la aplicación.

Nota: la velocidad de fotogramas no afecta al vídeo, ya que éste no está incorporado en la línea de tiempo. En cambio, el vídeo se carga dinámicamente mediante la transmisión o la descarga progresiva.

En este ejemplo, la velocidad de fotogramas se establece en un valor bajo de 10 fps. El temporizador actualiza los controles a una velocidad de una actualización por segundo. La velocidad de actualización más elevada es posible a través del método updateAfterEvent() , que está disponible en el objeto TimerEvent. Este método obliga a la pantalla a actualizarse cada vez que el temporizador distribuye un evento, si es necesario. El siguiente código ilustra la idea:

// Use a low frame rate for the application 
stage.frameRate = 10; 
  
// Choose one update per second 
var updateInterval:int = 1000; 
var myTimer:Timer = new Timer(updateInterval,0); 
  
myTimer.start(); 
myTimer.addEventListener( TimerEvent.TIMER, updateControls ); 
  
function updateControls( e:TimerEvent ):void 
{ 
    // Update controls here 
    // Force the controls to be updated on screen 
    e.updateAfterEvent(); 
}

La llamada al método updateAfterEvent() no modifica la velocidad de fotogramas. Simplemente obliga al motor de ejecución a actualizar el contenido en pantalla que se ha modificado. La línea de tiempo aún se ejecuta a 10 fps. Recuerde que los temporizadores y eventos ENTER_FRAME no son totalmente precisos en dispositivos de bajo rendimiento o si las funciones del controlador de eventos contienen código que requiere un gran procesamiento. Al igual que sucede con la velocidad de fotogramas de los archivos SWF, la velocidad de fotogramas de actualización del temporizador puede variar en algunas situaciones.

Reduzca el número de objetos Timer y controladores enterFrame registrados en la aplicación.

Cada fotograma, el motor de ejecución distribuye un evento enterFrame en cada objeto de visualización en su lista de visualización. Aunque se pueden registrar detectores para el evento enterFrame con varios objetos de visualización, esto significa que se ejecuta más código cada fotograma. Considere el uso de un solo controlador enterFrame centralizado que ejecute todo el código que se vaya a ejecutar en cada fotograma. Al centralizar este código, resulta más fácil administrar todo el código que se ejecuta frecuentemente.

Asimismo, si está utilizando objetos Timer, existe una sobrecarga asociada a la creación y distribución de eventos desde varios objetos Timer. Se deben activar diferentes operaciones en intervalos distintos; a continuación se incluyen algunas alternativas sugeridas:

  • Utilice un número mínimo de objetos Timer y operaciones de grupo según la frecuencia en que se produzcan..

    Por ejemplo, utilice un objeto Timer para operaciones frecuentes y defínalo para que se active cada 100 milisegundos. Utilice otro objeto Timer para operaciones en segundo plano menos frecuentes y defínalo para se active cada 2000 milisegundos.

  • Use un solo objeto Timer y establezca las operaciones para que se activen en múltiplos del intervalo de la propiedad delay del objeto Timer.

    Por ejemplo, supongamos que se tienen algunas operaciones que se espera que se produzcan cada 100 milisegundos y otras que se desea que sucedan cada 200 milisegundos. En ese caso, utilice un solo objeto Timer con un valor delay de 100 milisegundos. En el controlador de eventos timer , añada una sentencia condicional que sólo ejecute las operaciones de 200 milisegundos cada vez. En el siguiente ejemplo se demuestra esta técnica:

    var timer:Timer = new Timer(100); 
    timer.addEventListener(TimerEvent.Timer, timerHandler); 
    timer.start(); 
         
    var offCycle:Boolean = true; 
      
    function timerHandler(event:TimerEvent):void 
    { 
        // Do things that happen every 100 ms 
         
        if (!offCycle) 
        { 
            // Do things that happen every 200 ms 
        } 
         
        offCycle = !offCycle; 
    }
Detenga los objetos Timer cuando no se utilicen.

Si un controlador de eventos timer del objeto Timer sólo realiza operaciones en determinadas condiciones, llame al método stop() del objeto Timer cuando ninguna de las condiciones tenga el valor de true.

En el evento enterFrame o los controladores Timer, reduzca el número de cambios para mostrar el aspecto de los objetos de visualización que hacen que la pantalla deba redibujarse.

Con cada fotograma, la fase de representación vuelve a dibujar la parte del escenario que ha cambiado durante ese fotograma. Si la región de redibujo es extensa, o bien, si es pequeña pero contiene una gran cantidad de objetos de visualización complejos, el motor de ejecución necesita más tiempo para la representación. Para comprobar la cantidad de redibujo necesario, utilice la función “Mostrar regiones de redibujo” en la versión de depuración de Flash Player o AIR.

Para obtener más información sobre la mejora del rendimiento para acciones repetidas, consulte el siguiente artículo:

Síndrome de interpolación

Para ahorrar CPU, limite el uso de la interpolación, lo que ahorra procesamiento de CPU, memoria y duración de la batería.

Los diseñadores y desarrolladores que producen contenido para Flash en el escritorio, tienden a utilizar diversas interpolaciones de movimiento en sus aplicaciones. Al producir contenido para dispositivos móviles con bajo rendimiento, intente reducir el uso de las interpolaciones de movimiento. La limitación de su uso ayuda a que el contenido se ejecute más rápido en dispositivos de capa baja.