hand Inicio
Wallpaper

Cómo crear la herramienta Responsinator de kuworking

Aquí se trata de programar una herramienta de tipo responsinator para poder visualizar páginas web emulando distintos formatos de device

Prólogo

La utilidad de este tipo de herramientas es para desarrolladores web, para así poder visualizar rápidamente cómo queda su desarrollo en distintos formatos

Es decir, comprobar que la web es responsive

Puedes leer acerca de las herramientas que ya existen y funcionan muy bien en la entrada del blog aquí

Aquí implementaremos el nuestro en Gatsby

Vamos allá

Objetivos

Empezamos con los objetivos que buscamos:

  1. Crear una página en la que introduzcamos una dirección web y esta se cargue en una ventana determinada
  2. Esta ventana debe poder cambiar de medidas para emular móviles, tablets, desktops, etc
  3. Debe permitir visualizar el máximo de información con el mínimo de tiempo posible

Por lo que necesitaremos

  1. Un <input> para entrar la web, y un botón para activar el tema
  2. Un botón para cambiar de vistas (ver los distintos layouts en tamaños diferentes)
  3. Varios <iframes> para implementar las diferentes vistas

Esto lo haremos dentro de Gatsby, y utilizaremos styled-components (también tenemos la librería @emotion que en la práctica es intercambiable con styled-components)

Configurando Gatsby

Cojemos el starter oficial y lo instalamos

bash
gatsby new isresponsive https://github.com/gatsbyjs/gatsby-starter-hello-world

Nos vamos a la carpeta creada y clonada, y seguimos instalando los paquetes que necesitamos, básicamente styled-components

js
yarn add styled-components gatsby-plugin-styled-components babel-plugin-styled-components

Y añadimos el styled-components en ./gatsby-config.js

js
module.exports = {
siteMetadata: {
title: `Is Responsive`,
description: `Is Responsive de kuworking`,
author: `@kuworking`,
},
plugins: [`gatsby-plugin-styled-components`],
}

Código

Vamos al archivo principal ./src/pages/index.js para añadir nuestro componente IsResponsive

js
// ./src/pages/index.js
import React from 'react'

import { IsResponsive } from '../components/isresponsive'

const IndexPage = () => (
<div>
<IsResponsive />
</div>
)

export default IndexPage

Y ya podemos crear nuestro componente IsResponsive en ./src/components/isresponsive.js

js
// ./src/components/isresponsive.js
import React, { useState, useRef } from 'react'
import styled from 'styled-components'

export const IsResponsive = () => {
const main_maxwidth = '1080px'

// estado para definir la url a analizar
const [url, setUrl] = useState('https://www.kuworking.com/curso-javascript-basico/')

// estado para definiar la escala del iframe, implementado luego con css
const [commonScale, setCommonScale] = useState()

// referencia para el input donde se escribirán las urls
const inputRef = useRef()

const iframeArray = {
laptop: ['laptop', '1980', '1080', parseFloat(200 / 1980).toFixed(3), 5],
laptopSmall: ['laptop', '1440', '900', parseFloat(200 / 1440).toFixed(3), 5],
tablet: ['tablet', '768', '1024', parseFloat(200 / 768).toFixed(3), 15],
tabletInv: ['tablet', '1024', '768', parseFloat(200 / 1024).toFixed(3), 15],
mobile: ['mobile', '375', '812', parseFloat(200 / 375).toFixed(3), 20],
mobileSmall: ['mobile', '320', '568', parseFloat(200 / 320).toFixed(3), 20],
mobileSmallInv: ['mobile', '568', '320', parseFloat(200 / 568).toFixed(3), 20],
}

const toggle = () =>
setCommonScale((!commonScale && '0.5') || (commonScale === '0.5' && '1') || (commonScale === '1' && ''))

const init_test = () => {
const typed_url = inputRef.current.value.startsWith('http')
? inputRef.current.value
: 'https://' + inputRef.current.value
inputRef.current.value = typed_url
setUrl(typed_url)
}

return (
<>
<Input main_maxwidth={main_maxwidth}>
<Url ref={inputRef} placeholder="www.kuworking.com" />
<Button onClick={init_test}>⚡️</Button>
<Toggle onClick={toggle} commonScale={commonScale}>
💻
</Toggle>
</Input>

<Grid commonScale={commonScale}>
{Object.entries(iframeArray).map(([key, [label, w, h, scale, br]], i) => (
<Scroll key={'iframe' + i} commonScale={commonScale}>
<Label>
<span>{label}</span> {w} x {h}
</Label>

<Frame type={key} w={w} h={h} br={br} scale={commonScale ? commonScale : scale}>
<iframe
title={`iframe${i}`}
sandbox="allow-scripts allow-storage-access-by-user-activation allow-same-origin"
src={url}
/>
</Frame>
</Scroll>
))}
</Grid>
</>
)
}

const Input = styled.div`
margin-top: 20px;
margin-bottom: 40px;
display: flex;
max-width:
${props => props.main_maxwidth};
width: 100%;
padding: 0px 10px;
`

const Url = styled.input`
border: 1px solid #cecece;
width: 100%;
height: 40px;
border-radius: 3px;
background: #ececec;
padding: 5px;
font-size: 1.2rem;
text-align: center;
`

const Button = styled.div`
width: 40px;
height: 40px;
border-radius: 3px;
cursor: pointer;
box-shadow: 2px 3px 5px -2px #757575;
background: #ececec;
padding: 5px;
transition: background 0.2s ease;
margin-left: 10px;
display: flex;
align-items: center;
justify-content: center;

&:hover {
background: #ff6a00;
}
`


const Toggle = styled(Button)`
background:
${props =>
(!props.commonScale && '#4a98d6') ||
(props.commonScale === '0.5' && '#ff8d00') ||
(props.commonScale === '1' && '#ffdf00')}
;

&:hover {
background:
${props =>
(!props.commonScale && '#ff8d00') ||
(props.commonScale === '0.5' && '#ffdf00') ||
(props.commonScale === '1' && '#4a98d6')}
;
}
`


const Scroll = styled.div`
max-width: 90vw;
overflow: auto;
`


const Label = styled.div`
background: #989898;
color: #fff;
padding: 10px;
font-size: 0.8em;
border-radius: 8px;
margin-bottom: 20px;
text-transform: uppercase;

& > span {
font-weight: 700;
background: #000;
padding: 1px 4px;
}
`


const Frame = styled.div`
& > iframe {
transform: scale(
${props => (props.scale && props.scale) || '1'});
width: calc(100% /
${props => (props.scale && props.scale) || '1'});
height: calc(100% /
${props => (props.scale && props.scale) || '1'});
transform-origin: left top;
}
width: calc(
${props => props.w + 'px * ' + props.scale});
height: calc(
${props => props.h + 'px * ' + props.scale});
border: 5px solid #525252;
border-radius:
${props => props.br}px;
box-sizing: content-box;
box-shadow: 0px 0px 13px -4px #000;
overflow: hidden;
`


const Grid = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
grid-column-gap: 15px;
grid-row-gap: 15px;
width: min-content;
justify-items: baseline;
max-width:
${props => (props.commonScale ? 'unset' : '95%')};
`

Básicamente lo que hacemos es

  • Definir la url que analizaremos con un valor inicial
js
const [url, setUrl] = useState('https://www.kuworking.com/curso-javascript-basico/')
  • Definir un estado para la escala que nos servirá para escalar el iframe con css
js
const [commonScale, setCommonScale] = useState()
  • Crear un array para definir las medidas que queremos analizar, donde el valor de 200 es porque en la escala pequeña todas las medidas se normalizarán a 200px
js
const iframeArray = {
laptop: ['laptop', '1980', '1080', parseFloat(200 / 1980).toFixed(3), 5],
laptopSmall: ['laptop', '1440', '900', parseFloat(200 / 1440).toFixed(3), 5],
tablet: ['tablet', '768', '1024', parseFloat(200 / 768).toFixed(3), 15],
tabletInv: ['tablet', '1024', '768', parseFloat(200 / 1024).toFixed(3), 15],
mobile: ['mobile', '375', '812', parseFloat(200 / 375).toFixed(3), 20],
mobileSmall: ['mobile', '320', '568', parseFloat(200 / 320).toFixed(3), 20],
mobileSmallInv: ['mobile', '568', '320', parseFloat(200 / 568).toFixed(3), 20],
}
  • Luego implemento un botón para cambiar la escala y así cambiar las dimensiones de los iframes con toggle

  • Y finalmente la lógica para recoger los valores del <input>

js
const inputRef = useRef()

const init_test = () => {
const typed_url = inputRef.current.value.startsWith('http')
? inputRef.current.value
: 'https://' + inputRef.current.value
inputRef.current.value = typed_url
setUrl(typed_url)
}

El componente retorna todo el código JSX que no es más que el input, un botón para iniciar el análisis y otro para cambiar la escala de los iframes

Y luego el desplegar el objeto anterior iframeArray (que no era un array pero lo convertimos con Object.entries) para volcar iframes

El resto del código son los estilos y no tiene mayor misterio, excepto la parte que corresponde al iframe

js
const Frame = styled.div`
& > iframe {
transform: scale(
${props => (props.scale && props.scale) || '1'});
width: calc(100% /
${props => (props.scale && props.scale) || '1'});
height: calc(100% /
${props => (props.scale && props.scale) || '1'});
transform-origin: left top;
}
width: calc(
${props => props.w + 'px * ' + props.scale});
height: calc(
${props => props.h + 'px * ' + props.scale});
border: 5px solid #525252;
border-radius:
${props => props.br}px;
box-sizing: content-box;
box-shadow: 0px 0px 13px -4px #000;
overflow: hidden;
`

Básicamente, para cambiar las dimensiones utilizo la propiedad transform: scale() para hacerle un zoom, vendría a ser una emulación del viewport

Y luego adapto también el width y el height del iframe y del <div> padre

Mucho más sencillo de lo que podría parecer

🙋‍♂️

draw of me

Hola, tienes en mente desarrollar una web?

Si quieres, te ayudo

Escríbeme