اليوم عندما كنت أدرس كتاب "أساسيات الرسوم المتحركة HTML5+Javascript"، في القسم الثالث من الفصل الثامن، تحدثت عن كيفية استخدام ثلاث نوابض لتوصيل ثلاث نقاط لأداء حركات التمدد.
بعد الانتهاء من المثال، فكرت فيما إذا كان أربع نقاط أم خمس نقاط.
أعدت كتابة الكود وجعلت عدد النقاط متغيرًا. التأثير النهائي هو تحقيق حركة التمدد النهائية لكل نقطة لتحقيق التوازن، لكن الروابط بين النقاط ليست جيدة المظهر، وبعضها متقاطع.
لذلك كنت أفكر فيما إذا كان من الممكن تحسين هذه المنطقة.
تدوير الخطالنقاط في المثال السابق كلها في مواقع عشوائية، وبالتالي لا يمكن التحكم في الاتصالات. لذلك أريد أن أبدأ بهذا أولاً.
أولاً، استخدم نقطة معينة كنقطة مرجعية للحصول على زوايا النقاط الأخرى بالنسبة لهذه النقطة.
ثم قم بتوصيل هذه النقاط حسب الزاوية من الصغيرة إلى الكبيرة، لتتمكن من رسم مضلع عادي.
رمز التنفيذ التقريبي هو كما يلي:
Let balls = [];let ballNum = 6;let firstBall = null;while(ballNum--) { Let ball = new Ball(20, parseColor(Math.random() * 0xffffff)) ball.x = Math.random( ) * width; ball.y = Math.random() * height; balls.push(ball) if (!firstBall) { firstBall = ball ball.angle = 0 } else { const dx = ball.x - firstBall.x, dy = ball.y - firstBall.y; ball.angle = Math.atan2(dy, dx); = الكرات .sort((ballA, ballB) => {return ballA.angle - ballB.angle})
بهذه الطريقة، عندما يتم رسم الاتصال أخيرًا، يمكن رسم الزاوية من الصغيرة إلى الكبيرة عن طريق اجتياز المصفوفة.
التأثير هو كما يلي:
وهذا يمكن أن يقلل بشكل كبير من عدد خطوط العبور، ولكن لا يزال من غير الممكن تجنبه تمامًا.
بعد ذلك، أريد أن أحاول تحسين هذا الحل، على سبيل المثال، استخدم Math.abs لتصحيح الزاوية، أو ابحث عن النقطة ذات الزاوية الأصغر لتوصيل كل نقطة. لكن النتيجة ليست جيدة، ولا يمكن تجنب تجاوز الخطوط.
تدوير على أساس نقطة المركزثم طرأت على ذهني فكرة أخرى، فإذا كان من الممكن تحديد نقطة مركز المضلع، فيمكن حساب زوايا جميع النقاط بالنسبة لنقطة المركز بشكل منفصل، ويمكن ربط هذه النقاط في اتجاه عقارب الساعة أو عكس اتجاه عقارب الساعة.
ومع ذلك، بعد البحث لفترة طويلة على الإنترنت، تتطلب جميع خوارزميات النقاط سلسلة من النقاط مرتبة بترتيب معين في اتجاه عقارب الساعة.
لكن إذا كانت لدي هذه النقاط، فيمكنني رسم المضلع بالفعل. كان علي أن أستسلم
تجزئة المحور X ثنائي القطبفي حالة من اليأس، اضطررت إلى البحث في Google، ثم وجدت إجابة جيدة على Zhihu: كيفية ربط مجموعة من النقاط غير المنتظمة على المستوى بمضلع بسيط؟
للحصول على وصف الخوارزمية المحددة، ما عليك سوى إلقاء نظرة على الإجابة ولن أخوض في التفاصيل.
ومع ذلك، عند توصيل السلسلة العلوية والسلسلة السفلية، ما عليك سوى التأكد من توصيل السلسلة العلوية بترتيب تنازلي على المحور X والسلسلة السفلية متصلة بترتيب تصاعدي على المحور X (مرسومة في اتجاه عكس اتجاه عقارب الساعة) . أما بالنسبة للنقاط التي لها نفس المحور X فلا يهم إذا كان المحور Y أكبر أو أصغر.
عند التنفيذ، يتم تنفيذه بدقة وفقًا للخوارزمية الموجودة في الإجابة.
عند الحكم على ما إذا كانت نقطة ما تنتمي إلى السلسلة العليا أو السلسلة السفلية، فإن الفكرة الأولية هي تحديد معادلة دالة الخط المستقيم بناءً على نقطتين، ثم إدخال إحداثيات النقاط للحساب. لكنني اعتقدت لاحقًا أن جميع النقاط تستخدم القطب الموجود في أقصى اليسار لحساب الزاوية المائلة، ثم تقسمها وفقًا لحجم الزاوية، وهو ما يسهل فهمه بصريًا.
الرمز التقريبي هو كما يلي:
Let balls = [];let tempBalls = [];let ballNum = 6;let isDragingBall = false;while(ballNum--) { Let ball = new Ball(10, parseColor(Math.random() * 0xffffff)) ball. x = Math.random() * width; ball.y = Math.random() * height; دع النقاط يتم فرزها بترتيب تصاعدي على tempBalls.length -1];let SmallXBalls = tempBalls.filter(ball => ball.x === firstBall.x), bigXBalls = tempBalls.filter(ball => ball.x === lastBall.x)// تعامل مع الموقف الذي يوجد فيه عدة أقطاب يسار ويمين if (smallXBalls.length > 1) { SmallXBalls.sort((ballA, ballB) => { return ballB.y - ballA.y })}if (bigXBalls.length > 1) { bigXBalls.sort((ballA, ballB) => { return ballB.y - ballA.y })}firstBall = SmallXBalls[0]lastBall = bigXBalls[0]// احصل على زاوية اتصال القطب Let SplitLineAngle = Math.atan2(lastBall.y - firstBall.y, lastBall.x - firstBall.x);let UpperBalls = [], LowerBalls = [];// جميع النقاط الأخرى تحسب الزوايا باستخدام firstBall// أي شيء أكبر من SplitLineAngle هو سلسلة سفلية // والبعض الآخر موجود في سلسلة أعلى tempBalls.forEach(ball => { if (ball === firstBall || ball === lastBall) { return false } Let angle = Math.atan2(ball.y - firstBall.y, ball.x - firstBall.x); if (angle > SplitLineAngle) { LowerBalls.push(ball) } else { UpperBalls.push(ball) }})// التعامل مع فرز نفس الموقف على المحور X LowerBalls = LowerBalls.sort((ballA, ballB) => { if (ballA.x !== ballB.x) { return ballA.x - ballB.x } return ballB.y - ballA.y})upperBalls = UpperBalls.sort((ballA, ballB) => { if (ballA.x !== ballB.x) { return ballB.x - ballA.x } return ballB.y - ballB.x})// ربط جميع النقاط عكس اتجاه عقارب الساعة balls = [firstBall].concat(lowerBalls, [lastBall], UpperBalls)balls = balls. خريطة ((الكرة، i) => { ball.text = i + 1؛ كرة العودة})
الكرات التي تم إرجاعها أخيرًا هي نقاط المضلع مرتبة عكس اتجاه عقارب الساعة.
التأثير هو كما يلي:
الحالة الداخلية لكل كرة هي كما يلي:
ما ورد أعلاه هو المحتوى الكامل لهذه المقالة وآمل أن يكون مفيدًا لدراسة الجميع وآمل أيضًا أن يدعم الجميع شبكة VeVb Wulin.