كتابة Pixel Art Maker في JavaScript



يوم جيد يا اصدقاء!

مقدمة


مرة واحدة قادني تصفح الويب إلى هذا .

في وقت لاحق اكتشفت مقالة حول كيفية عمل ذلك.

لا يبدو أن هناك شيئًا مميزًا - بيكاتشو ، تم رسمه باستخدام CSS. تسمى هذه التقنية Pixel Art (فن البكسل؟). ما أدهشني هو تعقيد العملية. يتم رسم كل خلية يدويًا (حسنًا ، تقريبًا ؛ نظرًا لوجود معالجات مسبقة ؛ ساس في هذه الحالة). بالطبع الجمال يتطلب التضحية. ومع ذلك ، المطور هو مخلوق كسول. لذلك ، فكرت في الأتمتة. لذا ظهر ما أسميه Pixel Art Maker.

الظروف


ما الذي نريد الحصول عليه؟

نحن بحاجة إلى برنامج يولد عددًا معينًا من الخلايا مع إمكانية تلوينها بألوان عشوائية.

إليك بعض الأمثلة من الويب:


وظائف اضافيه:

  • شكل الخلية - مربع أو دائرة
  • عرض الخلية بالبكسل
  • عدد الخلايا
  • لون الخلفية
  • اللون للتلوين
  • وظيفة إنشاء قماش
  • وظيفة عرض رقم الخلية
  • وظيفة حفظ / حذف الصورة
  • وظيفة تنظيف قماش
  • وظيفة إزالة قماش

سنناقش تفاصيل أصغر في عملية الترميز.

إذن هيا بنا نذهب.

وضع علامة على


لتنفيذ الوظائف الضرورية ، يجب أن يبدو HTML الخاص بنا على النحو التالي:

<!--     -->
<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>
            <!--    10  50 ( ) -->
            <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>

تم تحديد نطاق (حد) القيم للعرض وعدد الخلايا تجريبيا. أظهرت التجارب أن القيم الأصغر / الأكبر غير عملية لأسباب التفصيل المفرط (للقيم <10 للعرض) ، وانخفاض الأداء (للقيم> 50 للكمية) ، إلخ.

الأنماط


ليس لدينا شيء خاص في الأنماط.

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;
}

جافا سكريبت


تحديد اللوحة وسياقها (سياق الرسم ثنائي الأبعاد):

let c = document.querySelector('canvas'),
    $ = c.getContext('2d')

نجد زر إنشاء اللوحة القماشية ونعلق معالج حدث النقر عليها:

document.querySelector('.generate').onclick = generateCanvas

كل الكود الإضافي سيكون في وظيفة generCanvas:

function generateCanvas(){
    ...
}

نحدد الشكل والعرض والمبلغ الأفقي والمبلغ الإجمالي (تمثل اللوحة نفس عدد الخلايا أفقيًا وعموديًا) ، بالإضافة إلى لون الخلفية:

// 
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

نحدد حجم اللوحة ونحدد السمات المناسبة لها (تذكر أن حجم اللوحة صحيحًا يتم تعيينه من خلال السمات):

//  =  =   *    
let W = H = shapeWidth * shapeNumber
c.setAttribute('width', W)
c.setAttribute('height', H)

بعض الإعدادات الإضافية:

//  
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()
}

إليك ما تبدو عليه دالة المربعات:

function squares() {
    //   
    let x = y = 0

    //  
    let squares = []

    //     ()
    let w = h = shapeWidth

    //    
    addSquares()

    // -
    function Square(x, y) {
        //   
        this.x = x
        //  y 
        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

            //       
            //   y   
            //      
            //   
            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()
    }
}

دالة الدوائر مشابهة جدًا لوظيفة المربعات.

جافا سكريبت:
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()
    }
}

نجد زر حفظ / حذف النتيجة (الصورة) وتعليق معالج حدث النقر عليه:

document.querySelector('.save').onclick = () => {
    //  
    let img = document.querySelector('img')

    //   , 
    //  , 
    img == null ? document.body.appendChild(document.createElement('img')).src = c.toDataURL() : document.body.removeChild(img)
}

نجد زر لتنظيف القماش و ...:

document.querySelector('.clear').onclick = () => {
    //    
    $.clearRect(0, 0, W, H)
    generateCanvas()
}

ابحث عن الزر لحذف اللوحة و ...:

document.querySelector('.delete').onclick = () => {
    $.clearRect(0, 0, W, H)
    c.style.display = 'none'
}

تبدو النتيجة كما يلي:



Codepen (تمت إضافة بضعة أمثلة)

Github

شكرًا لك على اهتمامك .

All Articles