ورشة تطبيق web - JavaScript (ثانيا: التحريك animation)


#1

هذا الموضوع سأبني عليه تحديات مستقبلية لنا جميعا -ان شاء الله- ،،،

الكود المستخدم تجدونه من هنا: https://codepen.io/nasr3090/pen/wjrYwe

نسخة من الكود بترتيبي الخاص
<!DOCTYPE html>

<html lang="en">
    
    <!-- ******** Starting head tag ******** -->
    <head>
        <meta charset="utf-8"/>
        <meta name="author" content="Nasr Galal" />
        <meta name="copyright" content="Nasr Galal 2018" />
        <title>Lesson 2: animation</title>
    </head>
    <!-- ******** ending head tag ******** -->
    
    
    <!-- ******** Setting page styles ******** -->
    <style>
        
        html {
          background: lightgrey;
        }         

        canvas {
            border: 1px solid black;
            display: block;
            margin-left: auto;
            margin-right: auto;
        }
        
    </style>
    <!-- ******** Ending page styles ******** -->
    
    
    <!-- ******** Starting body tag ******** -->
     <body>
        <!-- one canvas inbound -->
        <canvas id="meow" width="200px" height="200px">
            Hi there!
        </canvas> 
        <!-- ending canvas -->
        
        
        <!-- ******** Setting page scripts ******** -->
        <script>
            // ********************************* Starting variables section *********************************//
            // *************************************************** //
            //Naming variables to use for drawings
            var canvas, ctx, w, h;
            
            // ************************* Variables for location and movement *************************//        
            // as for rectangle location and speed          |           // as for circle location and speed
            //                                              |  
            var recX = 10;      /* location in x axis       |*/         var cirx = 100;    // location in x axis
            var recY = 10;      /* location in y axis       |*/         var ciry = 100;    // location in y axis
            var speed = 1;      /* speed                    |*/         var rad = 15;      // radius
            var rectWidth = 30;      /*Rectangle width      |*/         var spx = 2;       // speed in x axis
            var rectHeight = 30;    /*Rectangle height      |*/         var spy = 1;       // speed in y axis
            // ********************************* Ending variables section *********************************//
        
        
            // ******** Starting Rules for loading the content ********//
            // loading drawings when loading screen
            window.onload = function init() {
                //Defining The variables
                // a. Calling the canvas
                canvas = document.querySelector("#meow");
                // Saving width & height info.
                w = canvas.width;
                h = canvas.height;
            
                // b. getting the drawing tool
                ctx = canvas.getContext('2d');
             
                //setting Animation details into a main function as a way to organize content
                mainLoop();  // This will contain the animation details for all shapes
                };
            // ******** Ending Rules for loading the content ********//
        
        
            // ******** Functions Section ********//
            //This is the main loop we set to control all the functions...
            function mainLoop() {
                // 1- clearing canvas
                ctx.clearRect(0, 0, w, h);
                // drawing process    
                // a. filled rectangle
                filledRect(recX, recY, "red");
                // b. filled circle
                filledCircle(cirx, ciry, rad, "green");
            
                //animation movements
                //a. the rectangle
                recAnimation();     // this is to call the function For rectangle animation
                //--------------------
                // b. for the circle
                cirAnimation();     // this is to call the function For circle animation
            }
        
            //Rules for drawing the rectangle
            function filledRect(x, y, color) {
                //Saving the above tempelate (x, y, color) to be able to substitute them in line 61
                ctx.save();
                // translate coordinate system & draw relative to it
                ctx.translate(x, y);        // Check line 61 >> I substituted the x,y values with locations
                // defining the color so that we can substitute in line 61
                ctx.fillStyle = color;
                // width & height
                ctx.fillRect(0, 0, rectWidth, rectHeight); // x & y are set to 0 and I set width and height to be 30 in    each
                // restoring context to load all the above parameters
                ctx.restore();
            }
        
            // Rules for drawing the circle
            function filledCircle(x, y, rad, color) {
                //Saving the above tempelate (x, y, rad, color) to be able to substitute them in line 63
                ctx.save();
                // translate coordinate system & draw relatie to it
                ctx.translate(x, y);       // Check line 63 >> I substituted the x,y values with locations 
                //identifying the color so that we can substitute in line 61
                ctx.fillStyle = color;
                // identifying radius
                ctx.beginPath();
                ctx.arc(0, 0, rad, 0, 2*Math.PI);
                ctx.fill();
                //restoring
                ctx.restore();
            }
        
            //Rectangle animation rules
            function recAnimation() {
                recX += speed;     // adding speed to the location in x direction
                        
                // Testing collision against left and right walls
                if (((recX + rectWidth) > w)) {
                    speed = -speed;     //speed direction changes!
                }
                requestAnimationFrame(mainLoop); //getting new animation frame for the rectangle
            }
            
            //Circle animation rules
            function cirAnimation() {
                // I made the movement in x and y directions
                cirx += spx;
                ciry += spy;
                //collision with walls
                // 1. vertical walls --> right wall
                if ((cirx + rad) > canvas.width) {
                    spx = -spx;
                    cirx = w - rad;  //putting ball at collision point
                } else if ((cirx - rad) < 0) {
                    spx = -spx;
                    cirx = rad; //putting ball at collision point
                }
            
                // 2. horizontal walls --> right one
                if ((ciry + rad) > canvas.height) {
                    spy = -spy;
                    ciry = h - rad;
                } else if ((ciry - rad) < 0) {
                    spy = -spy;
                    ciry = rad;
                }
            }

     </script>
        <!-- ******** ending page scripts ******** -->
    
    </body>
    <!-- ******** ending body tag ******** -->
    
</html>

شكل النتيجة النهائية:
ezgif-1-d9861732a2

كما نشاهد في الصورة … المعادلات التي تحكم حركة الكرة والمستطيل هي رياضية بحته:
1- حركة المستطيل في اتجاه س أو x
2- حركة الكرة في المحورين (س، ص) أو (x, y)

الـfunctions أو الدوال المستخدمة:

سنقوم بعمل function رئيسية سمّيتها mainLoop تحتوي على الآتي:

  • هذا الكود ctx.clearRect(0, 0, w, h); لمسح الـcanvas بمحتوياته
    ثم إعادة رسمها من جديد كالآتي:
  • دالة لرسم المستطيل filledRect()
  • دالة لرسم الدائرة filledCircle()
  • دالة لتحريك المستطيل recAnimation()
  • دالة لتحريك الدائرة cirAnimation()

الأدوات المستخدمة :

أدوات الرسم: https://www.w3schools.com/tags/ref_canvas.asp

التطبيق

كود الmainLoop function

شكل كود ال mainLoop:

//This is the main loop we set to control all the functions...
            function mainLoop() {
                // 1- clearing canvas
                ctx.clearRect(0, 0, w, h);
                // drawing process    
                // a. filled rectangle
                filledRect(recX, recY, "red");
                // b. filled circle
                filledCircle(cirx, ciry, rad, "green");
            
                //animation movements
                //a. the rectangle
                recAnimation();     // this is to call the function For rectangle animation
                //--------------------
                // b. for the circle
                cirAnimation();     // this is to call the function For circle animation
            }

كما ترون هذه كلها استدعاءات للfunctions لكننا لم نقم ببرمجة الfunctions حتى الآن!
لكن هذه الطريقة في رأيي لتبسيط الكود حتى لا يكون طويل جدا … بحيث يمكن عزل كل function على حدة ويمكن استدعائها داخل functions اخرى كما نشاء، وفي أي وقت…

كود رسم المستطيل
//Rules for drawing the rectangle
            function filledRect(x, y, color) {
                //Saving the above tempelate (x, y, color) to be able to substitute them in line 61
                ctx.save();
                // translate coordinate system & draw relative to it
                ctx.translate(x, y);        // Check line 61 >> I substituted the x,y values with locations
                // defining the color so that we can substitute in line 61
                ctx.fillStyle = color;
                // width & height
                ctx.fillRect(0, 0, rectWidth, rectHeight); // x & y are set to 0 and I set width and height to be 30 in    each
                // restoring context to load all the above parameters
                ctx.restore();
            }

كما تعلمنا سابقا من ورشة عمل 1 … يمكننا رسم المستطيل مع تعديل بسيط…
نلاحظ أني كتبت عدة متغيرات هنا: function filledRect(x, y, color) والتي سأستخدمها كإطار عمل ثابت، ثم عرفتها كما يلي:
1- نحفظ هذا الترتيب بواسطة الدالة ctx.save(); ثم نطلب الكود ctx.translate(x, y); والذي يربط الحرفين x, y بنظام الاحداثيات ونلاحظ أني في الأعلى قمت بالتعويض مباشرة عند استدعاء الfunction داخل الmainLoop filledRect(recX, recY, "red"); وهي المتغيرات التي عرفتها سابقا:

var recX = 10;      /* location in x axis 
var recY = 10;      /* location in y axis

يمكنكم التعرف على المزيد عن ctx.save() من هنا: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore

يمكن ربط ال color بـأداة التلوين في الcanvas وهي ctx.fillstyle كما بالشكل:
ctx.fillStyle = color;
يتبقى رسم المستطيل مع اعطاء المقاسات ctx.fillRect(0, 0, rectWidth, rectHeight);
وأخيرا الدالة التي تستعيد كل شئ للوضع الافتراضي ctx.restore()
يمكنكم القراءة عنها من هنا: https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/restore

كود رسم الدائرة

بنفس الطريقة نبتكر function مسؤولة عن رسم الدائرة:

// Rules for drawing the circle
            function filledCircle(x, y, rad, color) {
                //Saving the above tempelate (x, y, rad, color) to be able to substitute them in line 63
                ctx.save();
                // translate coordinate system & draw relatie to it
                ctx.translate(x, y);       // Check line 63 >> I substituted the x,y values with locations 
                //identifying the color so that we can substitute in line 61
                ctx.fillStyle = color;
                // identifying radius
                ctx.beginPath();
                ctx.arc(0, 0, rad, 0, 2*Math.PI);
                ctx.fill();
                //restoring
                ctx.restore();
            }
كود تحريك المستطيل

كما نشاهد في الصورة ،، المستطيل يتحرك بشكل أفقي، بحيث تعتمد الحركة على الموقع بالنسبة لـ X وسرعته:
ezgif-1-d9861732a2

وبالمتغيرات التي عرفتها سابقا: var recX = 10; var speed = 1; ستكون قاعدة التحريك كالآتي:
recX = rexX + speed أو recX += speed

بالنسبة لتصادم المستطيل مع حوائط الcanvas ، دعوننا نعبر عنها بشكل رياضي ليسهل علينا تحويلها إلى كود… حيث أنه مرتبط باتجاه سرعة المستطيل عند كل طرف

الcanvas يتم رسمه من اليسار إلى اليمين كما شاهدنا في الصورة أعلاه، حيث أن الحائط الرأسي الأيسر = صفر
والأيمن = canvas.width
الآن نستطيع تحديد موقع الأشكال داخل الصندوق

موقع المستطيل في X وطوله أيضا > 0 (أي على يمين ضلع الcanvas الأيسر)
موقع المستطيل في X وطوله أيضا < w (أقل من canvas.width أي على يسار ضلع الcanvas الأيمن)

اذا استخدمنا if conditional ، يمكننا عمل شروط تحدد اتجاه السرعة للمستطيل كالتالي:

                if (((recX + rectWidth) > w) || (recX < 0)) {
                    speed = -speed;     //speed direction changes!
                }

هنا نشترط أنه اذا خرج موقع X أو طوله عن الضلع الأيمن فيتغير اتجاه السرعة الي اليسار،، واذا خرج طول المستطيل عن الضلع الايسر فيتغير اتجاه السرعة الى اليمين

كود تحريك الدائرة

نطبق نفس الفكرة على الدائرة … ونريدها ايضا تسير في الاتجاه y مع x >> الكود كالتالي:

// I made the movement in x and y directions
                cirx += spx;
                ciry += spy;
                //collision with walls
                // 1. vertical walls --> right wall
                if ((cirx + rad) > canvas.width) {
                    spx = -spx;
                    cirx = w - rad;  //putting ball at collision point
                } else if ((cirx - rad) < 0) {
                    spx = -spx;
                    cirx = rad; //putting ball at collision point
                }
            
                // 2. horizontal walls --> right one
                if ((ciry + rad) > canvas.height) {
                    spy = -spy;
                    ciry = h - rad;
                } else if ((ciry - rad) < 0) {
                    spy = -spy;
                    ciry = rad;
                }
            }

لقد نشرت الشرح بالفيديو على جروب اسرة المبادرة على الفيسبوك من هنا: https://www.facebook.com/groups/1793719174001339/permalink/1883387115034544/
آسف على طول الشرح… وأرجو أن يكون مفهوما…

غدا ان شاء الله سنبدأ أول تحدي معا :slight_smile:


تحدي اليوم! تحريك عدد من الكرات داخل الcanvas
#2

ممكن توضيح لهذا الامر في المستطيل, ايش عمله ؟
requestAnimationFrame(recLoop);


#3

هذا الأمر موجود في قاعدة البيانات داخل جافاسكريبت، وهو مسؤول عن التحريك… اذا حذف هذا الأمر، وقمت بتحديث الصفحة (تجد أن الحركة قد توقفت من جميع الأشكال) … بداخل هذا الأمر قمنا باستدعاء دالة (recLoop) بداخلها مجموعة أوامر مسؤولة عن التحريك…

يمكنك معرفة المزيد من هنا: https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame