Desarrollando un Módulo Framer

Framer Studio es una excelente herramienta de creación de prototipos para diseñadores. No solo permite prototipos sofisticados listos para usar, sino que sus capacidades se pueden ampliar a través de módulos. Encontrará muchos módulos excelentes en la Web, pero ¿qué sucede si desea crear uno propio?

Desafortunadamente, hay una gran brecha entre saber lo suficiente del lenguaje central de Framer Studio, CoffeeScript, para crear animaciones interactivas y poder producir incluso un módulo simple. Intentaremos cerrar esa brecha aquí.

Vamos a crear un módulo llamado "GhostLayer" que facilita el desvanecimiento de capas de forma interactiva. Haremos esto definiendo una clase que extienda la capa predeterminada. Esto significa que nuestra clase tendrá todas las propiedades habituales de una Capa: puede colocarla con x e y, darle una sombra paralela, etc., a la vez que habilita algunas funciones personalizadas que ofrecemos.

Puede seguirlo en su editor de texto preferido o en el propio Framer Studio. *

Cree un nuevo archivo de texto y guárdelo como "GhostLayer.coffee". Comenzaremos declarando lo que estamos haciendo. Escriba lo siguiente en su documento:

clase GhostLayer extiende Capa

Aquí estamos diciendo que vamos a crear una nueva clase de capa, llamada GhostLayer, en el objeto de exportaciones. Debido a que es solo una especie de Capa, heredará todas las capacidades habituales de una capa.

Cada vez que se crea una nueva entidad de tipo GhostLayer, se inicializará en la memoria. Cualquier cosa que queramos entrar en el estado inicial de GhostLayer debe declararse en el constructor de la clase. Agreguemos eso a continuación.

¬ constructor: (@ opciones = {}) ->

Tenga en cuenta que en CoffeeScript, la sangría es importante. Usaré el símbolo ¬ para indicar un nivel de sangría.

Verá el símbolo @ con frecuencia a medida que creamos este código. Es un acceso directo de CoffeeScript que significa "esto". @Options es algo así como un portapapeles que estamos reservando para usar dentro del módulo.

Nuestro GhostLayer necesita ser capaz de ser translúcido para ser de alguna utilidad. Aquí está nuestro constructor completo:

¬ constructor: (@ opciones = {}) ->
¬ ¬ @ options.translucent? = False
¬ ¬ super @options

Le hemos otorgado a nuestro GhostLayer una nueva propiedad que las capas generalmente no tienen: la de ser translúcida. Esta propiedad se llama booleana porque solo puede ser verdadera o falsa. Lo configuramos con? = Para indicar que el usuario puede o no proporcionar un valor. falso es nuestro valor predeterminado en ese caso.

Cualquier otra propiedad de clase que pueda agregar se insertará dentro del constructor antes de super @options. Esa super @options es lo que envuelve la inicialización y agrega nuestras características personalizadas a la capa. Solo asegúrese de que cada propiedad esté en su propia línea y siga la misma sangría y sintaxis.

Por el momento translúcido no hace nada. Necesitamos establecer qué efecto debe producir la propiedad dentro del constructor, según lo determinado por el nivel de sangría.

¬ ¬ si @ options.translucent es verdadero
¬ ¬ ¬ @ .opacity = 0.5

Recuerde, @ es equivalente a "esto". Dado que estamos dentro de la definición de la clase, "esto" significa el objeto de la clase en sí, el GhostLayer. Estamos configurando la opacidad de GhostLayer en 0.5 si la translucidez está activada.

Esta característica permitirá al usuario establecer la translucidez predeterminada en la creación de un nuevo GhostLayer. Escribiría el siguiente código en Framer Studio, que es equivalente a agregarlo al archivo app.coffee principal del prototipo (no agregue esto a su documento GhostLayer.coffee; esto es para el usuario de su módulo):

GhostLayer = requiere "GhostLayer"
myGhost = nuevo GhostLayer
¬ translúcido: verdadero

Sin embargo, es posible que deseemos permitir que el usuario cambie la translucidez en momentos distintos de la inicialización. Para hacer esto, necesitamos definir un captador y definidor para nuestra propiedad translúcida. Esto también se realiza fuera del constructor pero aún dentro de la definición de clase.

¬ @define 'translúcido',
¬ ¬ obtener: ->
¬ ¬ ¬ @ options.translucent
¬ ¬ conjunto: (valor) ->
¬ ¬ ¬ @ options.translucent = value

Este es un patrón típico para captadores y colocadores. Tenga en cuenta que la cadena en la instrucción @define debe coincidir exactamente con el nombre de la opción que afectan al captador y al definidor.

Nuestro captador y definidor permitirá al usuario leer el estado actual de la propiedad translúcida utilizando

imprima myGhost.translucent

o cambiarlo usando

myGhost.translucent = true

Pero aunque la propiedad se actualizará con un valor verdadero, esto no tendrá ningún efecto visible en GhostLayer. Necesitamos agregar más código al configurador para asegurarnos de que los resultados visuales siempre estén sincronizados con el valor de la propiedad.

¬ @define 'translúcido',
¬ ¬ obtener: ->
¬ ¬ ¬ @ options.translucent
¬ ¬ conjunto: (valor) ->
¬ ¬ ¬ @ options.translucent = value
¬ ¬ ¬ si @ options.translucent es verdadero
¬ ¬ ¬ ¬ @ .opacity = 0.5
¬ ¬ ¬ más
¬ ¬ ¬ ¬ @ .opacity = 1.0

"Esto" (la @) en el ámbito léxico de nuestro módulo sigue siendo el objeto de capa GhostLayer. Estamos configurando la opacidad de GhostLayer al 50% si translúcido es verdadero o al 100% si no lo es.

El alcance léxico puede ser algo difícil de entender. Para verlo un poco más, hagamos que nuestro GhostLayer sea un poco más interactivo. Cuando se hace clic en GhostLayer, queremos que se desvanezca a su estado translúcido u opaco, dependiendo.

Regrese al constructor y agregue un controlador de eventos onClick que activará una función llamada fade (). (No necesitaremos los paréntesis aquí, pero los necesitará si llama a la función desde fuera del módulo).

¬ constructor: (@ opciones = {}) ->
¬ ¬ @ options.translucent? = False
¬ ¬ super @options
¬ ¬ @ .onClick @fade

@ .onClick significa "cuando se hace clic en esto" - "esto" significa el GhostLayer una vez más.

Debajo de la declaración @define y dentro de la clase (es decir, sangrar una vez), definiremos nuestra función fade ().

¬ desvanecerse: ->

El contenido de fade () se asemeja a lo que proporcionamos para el setter anterior, excepto al revés; estamos alternando entre los estados translúcido y opaco. Además, debemos mantener la propiedad booleana sincronizada con el resultado visual mientras lo hacemos.

¬ desvanecerse: ->
¬ ¬ si @ options.translucent es verdadero
¬ ¬ ¬ @ options.translucent = false
¬ ¬ ¬ @ .animate
Propiedades de ¬ ¬ ¬ ¬:
¬ ¬ ¬ ¬ ¬ opacidad: 1.0
¬ ¬ más
¬ ¬ ¬ @ options.translucent = true
¬ ¬ ¬ @ .animate
Propiedades de ¬ ¬ ¬ ¬:
¬ ¬ ¬ ¬ ¬ opacidad: 0.5

Esto funcionará lo suficientemente bien y producirá un agradable efecto animado. Pero, ¿qué pasaría si fuéramos un poco más complejos? Tal vez queremos asegurarnos de que la capa no esté en medio de la animación cuando activemos el cambio de opacidad. Después de todo, eso podría producir un efecto de parpadeo indeseable. Framer proporciona un mecanismo solo para este propósito, llamado rebote. Se ve así:

Utils.debounce 0.5, ->

El intervalo de tiempo (0.5 aquí durante medio segundo) es una especie de "enfriamiento". La función no se ejecutará nuevamente hasta que el enfriamiento haya expirado, incluso si se activa mientras tanto.

Agregar eso a fade () se ve así:

¬ fade: Utils.debounce 0.5, ->
¬ ¬ si @ options.translucent es verdadero
¬ ¬ ¬ @ options.translucent = false
¬ ¬ ¬ @ .animate
Propiedades de ¬ ¬ ¬ ¬:
¬ ¬ ¬ ¬ ¬ opacidad: 1.0
¬ ¬ más
¬ ¬ ¬ @ options.translucent = true
¬ ¬ ¬ @ .animate
Propiedades de ¬ ¬ ¬ ¬:
¬ ¬ ¬ ¬ ¬ opacidad: 0.5

Excelente. Pero arrojémonos una bola curva de ámbito léxico. Intente introducir un ligero retraso en la función:

¬ fade: Utils.debounce 0.5, ->
¬ ¬ Utils.delay 0.5, ->
¬ ¬ ¬ si @ options.translucent es verdadero
¬ ¬ ¬ ¬ @ options.translucent = false
¬ ¬ ¬ ¬ @ .animate
Propiedades de ¬ ¬ ¬ ¬ ¬:
¬ ¬ ¬ ¬ ¬ ¬ opacidad: 1.0
¬ ¬ ¬ más
¬ ¬ ¬ ¬ @ options.translucent = true
¬ ¬ ¬ ¬ @ .animate
Propiedades de ¬ ¬ ¬ ¬ ¬:
¬ ¬ ¬ ¬ ¬ ¬ opacidad: 0.5

Ahora tenemos un problema. Al hacer una llamada al contexto global Utils dentro de la función, hemos cambiado nuestro alcance léxico actual. Ya no estamos dentro del alcance de la función. En su lugar, hemos rebotado a un alcance más global que no conoce cosas como @ options.translucent. Si deja el módulo de esta manera e intenta usarlo en Framer Studio, obtendrá errores sobre cosas que no están definidas.

Afortunadamente, la solución es simple. Obligamos a Framer a mantener el alcance léxico al cambiar una de nuestras flechas a una flecha “gorda” que mantenga el alcance. Busque el => a continuación:

¬ fade: Utils.debounce 0.5, ->
¬ ¬ Utils.delay 0.5, =>
¬ ¬ ¬ si @ options.translucent es verdadero
¬ ¬ ¬ ¬ @ options.translucent = false
¬ ¬ ¬ ¬ @ .animate
Propiedades de ¬ ¬ ¬ ¬ ¬:
¬ ¬ ¬ ¬ ¬ ¬ opacidad: 1.0
¬ ¬ ¬ más
¬ ¬ ¬ ¬ @ options.translucent = true
¬ ¬ ¬ ¬ @ .animate
Propiedades de ¬ ¬ ¬ ¬ ¬:
¬ ¬ ¬ ¬ ¬ ¬ opacidad: 0.5

Nuestra función de desvanecimiento volverá a funcionar como se esperaba.

Ahora, agregue la siguiente línea al final para terminar todo:

module.exports = GhostLayer

Las exportaciones son otro tipo de portapapeles compartido, pero al que también puede acceder un prototipo que aloja el módulo. Cualquier variable o función que viva en exportaciones estará disponible para el prototipo. Queremos poder usar la clase GhostLayer, por lo que nos aseguramos de hacer referencia a ella aquí.

Eso es suficiente para un módulo de trabajo. Para usarlo en un prototipo, cree un nuevo documento en Framer Studio. Guarde el documento en un directorio local y luego abra la carpeta .framer allí. Busque la carpeta "módulos" dentro. Ahí es donde debe copiar nuestro módulo GhostLayer.coffee. Una vez que lo hayas hecho, agrega el siguiente código a tu prototipo:

GhostLayer = requiere "GhostLayer"
myGhost = nuevo GhostLayer
¬ translúcido: falso

(Puede usar translúcido: verdadero si lo desea. La capa será más difícil de ver).

Intenta hacer clic en GhostLayer varias veces y verifica la animación. Recuerde, GhostLayer es solo una especie de capa. Puedes hacer cualquier cosa con ella que harías con una capa normal. Siéntase libre de asignar un color de fondo, reposicionarlo, etc.

Puede descargar una copia del módulo aquí o el prototipo completo aquí.

Además, si está creando prototipos para Apple TV, tenemos un ingenioso módulo RemoteLayer que debe consultar.

* Framer Studio tropieza con la palabra clave de exportaciones. Si lo omite, puede escribir su módulo en Framer Studio y beneficiarse de su autocompletado y verificación de errores. Deberá volver a agregarlo para completar el módulo. Tiendo a comentar la línea de exportaciones durante el desarrollo, luego descomento antes de pegar el módulo en su propio archivo .coffee.

Para obtener más información sobre el diseño y el desarrollo, suscríbase a BPXL Craft y siga a Black Pixel en Twitter.