隆Buen dia amigos!Prefacio
Una vez que la navegaci贸n web me llev贸 a esto .M谩s tarde descubr铆 un art铆culo sobre c贸mo funciona esto.No parecer铆a nada especial: Pikachu, dibujado con CSS. Esta t茅cnica se llama Pixel Art (驴pixel art?). Lo que me llam贸 la atenci贸n fue la complejidad del proceso. Cada celda est谩 pintada a mano (bueno, casi; ya que hay preprocesadores; Sass en este caso). Por supuesto, la belleza requiere sacrificio. Sin embargo, el desarrollador es una criatura perezosa. Por lo tanto, pens茅 en la automatizaci贸n. Entonces apareci贸 lo que llam茅 Pixel Art Maker.Condiciones
驴Qu茅 queremos conseguir?Necesitamos un programa que genere un n煤mero determinado de celdas con la posibilidad de colorearlas con colores arbitrarios.Aqu铆 hay un par de ejemplos de la web:Funciones adicionales:- forma de celda: cuadrada o circular
- ancho de celda en p铆xeles
- n煤mero de celdas
- color de fondo
- color para colorear
- funci贸n de creaci贸n de lienzo
- funci贸n de visualizaci贸n del n煤mero de celda
- funci贸n de guardar / borrar imagen
- funci贸n de limpieza de lonas
- funci贸n de eliminaci贸n de lienzo
Discutiremos detalles m谩s peque帽os en el proceso de codificaci贸n.Entonces vamos.Margen
Para implementar la funcionalidad necesaria, nuestro HTML deber铆a verse as铆:
<div class="tools">
<div>
<p>Shape Form</p>
<select>
<option value="squares">Square</option>
<option value="circles">Circle</option>
</select>
</div>
<div class="numbers">
<div>
<p>Shape Width <br> <span>(from 10 to 50)</span></p>
<input type="number" value="20" class="shapeWidth">
</div>
<div>
<p>Shape Number <br> <span>(from 10 to 50)</span></p>
<input type="number" value="30" class="shapeNumber">
</div>
</div>
<div class="colors">
<div>
<p>Background Color</p>
<input type="color" value="#ffff00" required class="backColor">
</div>
<div>
<p>Shape Color</p>
<input type="color" value="#0000ff" class="shapeColor">
</div>
</div>
<div class="buttons">
<input type="button" value="Generate Canvas" class="generate">
<input type="button" value="Show/Hide Numbers" class="show">
<input type="button" value="Save/Delete Image" class="save">
<input type="button" value="Clear Canvas" class="clear">
<input type="button" value="Delete Canvas" class="delete">
</div>
</div>
<canvas></canvas>
El rango (l铆mite) de valores para el ancho y el n煤mero de celdas se determin贸 emp铆ricamente. Los experimentos mostraron que los valores m谩s peque帽os / m谩s grandes no son pr谩cticos por razones de detalles excesivos (para valores <10 para ancho), rendimiento disminuido (para valores> 50 para cantidad), etc.Estilos
No tenemos nada especial en estilos.CSS:* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
margin: 0;
min-height: 100vh;
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
align-content: flex-start;
}
h1 {
width: 100%;
text-align: center;
font-size: 2.4em;
color: #222;
}
.tools {
height: 100%;
display: inherit;
flex-direction: column;
margin: 0;
font-size: 1.1em;
}
.buttons {
display: inherit;
flex-direction: column;
align-items: center;
}
div {
margin: .25em;
text-align: center;
}
p {
margin: .25em 0;
user-select: none;
}
select {
padding: .25em .5em;
font-size: .8em;
}
input,
select {
outline: none;
cursor: pointer;
}
input[type="number"] {
width: 30%;
padding: .25em 0;
text-align: center;
font-size: .8em;
}
input[type="color"] {
width: 30px;
height: 30px;
}
.buttons input {
width: 80%;
padding: .5em;
margin-bottom: .5em;
font-size: .8em;
}
.examples {
position: absolute;
top: 0;
right: 0;
}
a {
display: block;
}
span {
font-size: .8em;
}
canvas {
display: none;
margin: 1em;
cursor: pointer;
box-shadow: 0 0 1px #222;
}
Javascript
Defina el lienzo y su contexto (contexto de dibujo 2D):let c = document.querySelector('canvas'),
$ = c.getContext('2d')
Encontramos el bot贸n para crear el lienzo y colgamos el controlador de eventos de clic en 茅l:document.querySelector('.generate').onclick = generateCanvas
Todo el c贸digo adicional estar谩 en la funci贸n generateCanvas:function generateCanvas(){
...
}
Determinamos la forma, el ancho, la cantidad horizontal y la cantidad total (el lienzo representa la misma cantidad de celdas horizontal y verticalmente), as铆 como el color de fondo:
let shapeForm = document.querySelector('select').value
let shapeWidth = parseInt(document.querySelector('.shapeWidth').value)
let shapeNumber = parseInt(document.querySelector('.shapeNumber').value)
let shapeAmount = Math.pow(shapeNumber, 2)
let backColor = document.querySelector('.backColor').value
Determinamos el tama帽o del lienzo y establecemos los atributos apropiados para 茅l (recuerde que el tama帽o correcto del lienzo se establece a trav茅s de los atributos):
let W = H = shapeWidth * shapeNumber
c.setAttribute('width', W)
c.setAttribute('height', H)
Algunas configuraciones adicionales:
let border = 1
let borderColor = 'rgba(0,0,0,.4)'
let isShown = false
if (shapeWidth < 10 || shapeWidth > 50 || shapeNumber < 10 || shapeNumber > 50 || isNaN(shapeWidth) || isNaN(shapeNumber)) {
throw new Error(alert('wrong number'))
} else if (shapeForm == 'squares') {
c.style.display = 'block'
squares()
} else {
c.style.display = 'block'
circles()
}
As铆 es como se ve la funci贸n de cuadrados:function squares() {
let x = y = 0
let squares = []
let w = h = shapeWidth
addSquares()
function Square(x, y) {
this.x = x
this.y = y
this.color = backColor
this.isSelected = false
}
function addSquares() {
for (let i = 0; i < shapeAmount; i++) {
let square = new Square(x, y)
x += w
if (x == W) {
y += h
x = 0
}
squares.push(square)
}
drawSquares()
}
function drawSquares() {
$.clearRect(0, 0, W, H)
for (let i = 0; i < squares.length; i++) {
let square = squares[i]
$.beginPath()
$.rect(square.x, square.y, w, h)
$.fillStyle = square.color
$.lineWidth = border
$.strokeStyle = borderColor
$.fill()
$.stroke()
if (isShown) {
$.beginPath()
$.font = '8pt Calibri'
$.fillStyle = 'rgba(0,0,0,.6)'
$.fillText(i + 1, square.x, (square.y + 8))
}
}
}
c.onclick = select
function select(e) {
let clickX = e.pageX - c.offsetLeft,
clickY = e.pageY - c.offsetTop
for (let i = 0; i < squares.length; i++) {
let square = squares[i]
if (clickX > square.x && clickX < (square.x + w) && clickY > square.y && clickY < (square.y + h)) {
if (square.isSelected == false) {
square.isSelected = true
square.color = document.querySelector('.shapeColor').value
} else {
square.isSelected = false
square.color = backColor
}
drawSquares()
}
}
}
document.querySelector('.show').onclick = showNumbers
function showNumbers() {
if (!isShown) {
isShown = true
for (let i = 0; i < squares.length; i++) {
let square = squares[i]
$.beginPath()
$.font = '8pt Calibri'
$.fillStyle = 'rgba(0,0,0,.6)'
$.fillText(i + 1, square.x, (square.y + 8))
}
} else {
isShown = false
}
drawSquares()
}
}
La funci贸n de c铆rculos es muy similar a la funci贸n de cuadrados.JavaScript:function circles() {
let r = shapeWidth / 2
let x = y = r
let circles = []
addCircles()
function Circle(x, y) {
this.x = x
this.y = y
this.color = backColor
this.isSelected = false
}
function addCircles() {
for (let i = 0; i < shapeAmount; i++) {
let circle = new Circle(x, y)
x += shapeWidth
if (x == W + r) {
y += shapeWidth
x = r
}
circles.push(circle)
}
drawCircles()
}
function drawCircles() {
$.clearRect(0, 0, W, H)
for (let i = 0; i < circles.length; i++) {
let circle = circles[i]
$.beginPath()
$.arc(circle.x, circle.y, r, 0, Math.PI * 2)
$.fillStyle = circle.color
$.strokeStyle = borderColor
$.lineWidth = border
$.fill()
$.stroke()
if (isShown) {
$.beginPath()
$.font = '8pt Calibri'
$.fillStyle = 'rgba(0,0,0,.6)'
$.fillText(i + 1, (circle.x - 8), circle.y)
}
}
}
c.onclick = select
function select(e) {
let clickX = e.pageX - c.offsetLeft,
clickY = e.pageY - c.offsetTop
for (let i = 0; i < circles.length; i++) {
let circle = circles[i]
let distanceFromCenter = Math.sqrt(Math.pow(circle.x - clickX, 2) + Math.pow(circle.y - clickY, 2))
if (distanceFromCenter <= r) {
if (circle.isSelected == false) {
circle.isSelected = true
circle.color = document.querySelector('.shapeColor').value
} else {
circle.isSelected = false
circle.color = backColor
}
drawCircles()
}
}
}
document.querySelector('.show').onclick = showNumbers
function showNumbers() {
if (!isShown) {
isShown = true
for (let i = 0; i < circles.length; i++) {
let circle = circles[i]
$.beginPath()
$.font = '8pt Calibri'
$.fillStyle = 'rgba(0,0,0,.6)'
$.fillText(i + 1, (circle.x - 8), circle.y)
}
} else {
isShown = false
}
drawCircles()
}
}
Encontramos el bot贸n para guardar / eliminar el resultado (imagen) y colgar el controlador de eventos de clic:document.querySelector('.save').onclick = () => {
let img = document.querySelector('img')
img == null ? document.body.appendChild(document.createElement('img')).src = c.toDataURL() : document.body.removeChild(img)
}
Encontramos el bot贸n para limpiar el lienzo y ...:document.querySelector('.clear').onclick = () => {
$.clearRect(0, 0, W, H)
generateCanvas()
}
Encuentra el bot贸n para eliminar el lienzo y ...:document.querySelector('.delete').onclick = () => {
$.clearRect(0, 0, W, H)
c.style.display = 'none'
}
El resultado se ve as铆:
Codepen (agreg贸 un par de casos de uso)GithubGracias por su atenci贸n.