ในผืนผ้าใบคุณสามารถวาดวงกลมได้อย่างง่ายดายโดยใช้วิธี ARC ในขั้นต้นวงกลมยังถือได้ว่าเป็นวงรีที่มีความกว้างและความสูงเท่ากัน แต่ไม่มีวิธีที่จะวาดวงรีในผืนผ้าใบ เราจำเป็นต้องใช้วิธีการอื่นเพื่อจำลอง
ก่อนอื่นเราต้องชี้แจงพารามิเตอร์ที่จำเป็นในการวาดวงรี ความรู้ทางเรขาคณิตพื้นฐานบอกเราว่าวงรีต้องการพิกัดกลางความกว้างความสูงหรือมุมการหมุน แต่สามารถหลีกเลี่ยงได้ในขณะนี้การหมุนค่อนข้างง่าย
1. ใช้ lineto เพื่อวาดวงรี คุณอ่านว่าถูกต้อง lineto วิธีที่ใช้อย่างหมดจดในการวาดเส้นตรงสามารถใช้เพื่อวาดรูปไข่! - แต่เขามีอยู่จริง แต่สไตล์การเขียนนั้นเหลือเชื่อจริงๆ:ฟังก์ชั่น drawellipse (Canvas, O, OA, OB) {
// วาดวงรีตัวอย่าง: var b = อาร์เรย์ใหม่ (150,150); Drawellipse (HB, B, 50,30);
ด้วย (ผ้าใบ) {
var x = o [0]+OA;
var y = o [1];
Moveto (x, y);
สำหรับ (var i = 0; i <= 360; i ++) {
var ii = i*math.pi/180;
var x = o [0]+OA*math.cos (ii);
var y = o [1] -ob*math.sin (ii);
Lineto (x, y);
-
-
-
หลักการของวิธีนี้คือวงกลมมี 360 องศาดังนั้นใช้ lineto เพื่อวน 360 ครั้งวาดส่วนบรรทัดของแต่ละองศาและในที่สุดก็เชื่อมต่อพวกเขาเป็นวงรี ในหมู่พวกเขาฟังก์ชั่นตรีโกณมิติไซน์โคไซน์จำเป็นต้องใช้สำหรับการคำนวณ
โปรดทราบว่าพารามิเตอร์ที่สองของวิธีนี้คืออาร์เรย์นั่นคือพิกัดกลางของวงรี
ความคิดนั้นแปลกมากและรูปวงรีที่วาดนั้นค่อนข้างราบรื่น แต่มันไม่คุ้มค่าที่จะใช้ - วิธีนี้ต้องใช้ 360 รอบสำหรับการวาดรูปวงรีทุกครั้ง เฉพาะเมื่อรูปวงรีถูกวาดเป็นการทดสอบประสิทธิภาพของเบราว์เซอร์เพียงเล็กน้อย
เราแค่ต้องเข้าใจความคิดของเขา
2. ใช้อาร์คเพื่อวาดวงกลมและปรับขนาดเป็นวงรี ข้อความต้นฉบับของวิธีนี้อยู่ที่นี่และฟังก์ชั่นหลักมีดังนี้:var canvas = document.getElementById ('mycanvas');
var context = canvas.getContext ('2d');
var centerx = 0;
Var Centery = 0;
var radius = 50;
// บันทึกสถานะ
บริบท save ();
// แปลบริบท
Context.translate (Canvas.Width / 2, Canvas.Height / 2);
// สเกลบริบทในแนวนอน
Context.scale (2, 1);
// วาดวงกลมซึ่งจะยืดออกเป็นวงรี
Context.BeginPath ();
context.arc (centerx, centery, รัศมี, 0, 2 * math.pi, false);
// กู้คืนสู่สถานะเดิม
บริบท restore ()
วิธีนี้ใช้ฟังก์ชันผ้าใบที่ฉันไม่เคยพูดถึงมาก่อนนั่นคือมาตราส่วนซึ่งสามารถตระหนักถึงการปรับขนาดของผืนผ้าใบ การปรับขนาดมีสองทิศทาง: แนวนอนและแนวตั้ง รหัสขยายทิศทางแนวนอนของผ้าใบในขณะที่ทิศทางแนวตั้งยังคงไม่เปลี่ยนแปลง ดังนั้นวงกลมที่วาดโดยอาร์คจึงเปลี่ยนเป็นวงรี
วิธีนี้ยอดเยี่ยมมากเมื่อมองแวบแรกโดยมีรหัสน้อยและหลักการนั้นง่ายและเข้าใจง่าย แต่หลังจากวิเคราะห์แล้วคุณจะพบข้อบกพร่องที่ชัดเจนของเขานั่นคือ - ไม่ถูกต้อง! ตัวอย่างเช่นฉันต้องการวงรีที่กว้าง 171 และสูง 56 หากเราตั้งรัศมีของอาร์คเป็น 28 ดังนั้นจำนวนที่ตามมาของ 171/28/2 จะถูกกดดัน
อย่างไรก็ตามมีวิธีการประนีประนอมที่จะตั้งค่ารัศมีของอาร์คเป็น 100 เสมอและถ้ามันไม่เพียงพอมันจะขยายใหญ่ขึ้นและถ้ามันเกินกว่ามันจะลดลง อย่างไรก็ตามมันยังไม่ถูกต้อง
3. ใช้ bezier curveto เนื่องจากฉันรู้สึกว่าวิธีการปรับขนาดด้านบนไม่ถูกต้องฉันจึงต้องการหาวิธีที่แน่นอนในการวาดจุดไข่ปลาและในที่สุดก็พบมันบน stackoverflow:ฟังก์ชั่น drawellipse (ctx, x, y, w, h) {
var kappa = 0.5522848;
ox = (w / 2) * คัปปา, // จุดควบคุมออฟเซ็ตแนวนอน
oy = (h / 2) * kappa, // จุดควบคุมออฟเซ็ตแนวตั้ง
xe = x + w, // x-end
ye = y + h, // y-end
xm = x + w / 2, // x-middle
ym = y + h / 2; // y-middle
ctx.beginpath ();
ctx.moveto (x, ym);
ctx.beziercurveto (x, ym - oy, xm - ox, y, xm, y);
ctx.beziercurveto (xm + ox, y, xe, ym - oy, xe, ym);
ctx.beziercurveto (xe, ym + oy, xm + ox, ye, xm, ye);
ctx.beziercurveto (xm - ox, ye, x, ym + oy, x, ym);
ctx.closepath ();
ctx.stroke ();
-
วิธีนี้ถือได้ว่าสมบูรณ์แบบ เขาแบ่งวงรีออกเป็น 4 เส้นโค้ง Bezier และเชื่อมต่อกับวงรี ความกว้างและความสูงสุดท้ายก็ค่อนข้างแม่นยำและค่าใช้จ่ายน้อยกว่า
แต่วิธีนี้ยังคงมีข้อเสีย ทุกคนดูพารามิเตอร์คัปปาและมีค่าที่แปลกมาก ฉันเชื่อว่าหลายคนไม่เข้าใจว่าทำไมพวกเขาถึงต้องใช้คุณค่านี้ก่อนที่ผู้เชี่ยวชาญด้านเรขาคณิตจะบอกคุณว่าทำไมพวกเขาถึงต้องการใช้คุณค่านี้ - ฉันยังไม่รู้ และฉันมีความต้องการที่จะเปลี่ยนแปลงเขาและดูว่าผลที่ตามมาคืออะไร
แน่นอนว่าแรงกระตุ้นของฉันคล้ายกับผู้ป่วยโรคที่ครอบงำไม่สามารถกล่าวได้ว่าเป็นข้อเสียของวิธีนี้ ข้อเสียที่แท้จริงของเขาคือ - ทำไมคุณต้องใช้ 4 bezier curves? โดยส่วนตัวแล้วฉันคิดว่าวงรีนั้นประกอบไปด้วยเส้นโค้ง Bezier สองเส้นมากกว่าสี่ ในที่สุดความคิดนี้ทำให้ฉันค้นหาวิธีที่สมบูรณ์แบบที่สุดในการวาดวงรี
4. ใช้สองเส้นโค้งเพื่อวาดวงรี
เพื่อที่จะเข้าใจว่าวิธีการก่อนหน้านี้สามารถปรับปรุงได้หรือไม่ฉันได้ลงทะเบียนบัญชี Stackoverflow เป็นพิเศษเพื่อถามคำถาม เนื่องจากมีรูปภาพในคำถามจึงไม่สามารถส่งคะแนนได้ ฉันต้องใช้ความสามารถภาษาอังกฤษแทบจะไม่ตอบคำถามของชาวต่างชาติเพื่อรับคะแนน แต่ในที่สุดขอให้โชคดีมาและตอบคำถามแก้ไขปัญหาของฉัน
ฉันได้ยกปัญหาความสัมพันธ์ระหว่าง Besel Curve และ Ellipse
พูดตามตรงฉันไม่เข้าใจคำตอบของชาวต่างชาติส่วนใหญ่ด้านล่าง แต่โชคดีที่เขาได้จัดเตรียมหน้าตัวอย่างรหัสซึ่งทำให้ฉันเข้าใจหลักการและฉันขอขอบคุณเขาอีกครั้ง ตามคำตอบของเขาวิธีที่ฉันพบสำหรับการวาดรูปไข่มีดังนี้:
//วงรี
CanvasrenderingContext2d.prototype.oval = function (x, y, ความกว้าง, ความสูง) {
var k = (ความกว้าง/0.75)/2
w = ความกว้าง/2
h = ความสูง/2;
this.beginpath ();
this.moveto (x, yh);
this.beziercurveto (x+k, yh, x+k, y+h, x, y+h);
this.beziercurveto (xk, y+h, xk, yh, x, yh);
this.closepath ();
คืนสิ่งนี้;
-
วิธีนี้มีความแม่นยำและมีรหัสน้อยและไม่มีความแปลกและยากที่จะเข้าใจ เพียงจำไว้ว่าอัตราส่วนพิกัดของความกว้างของวงรีไปยังจุดควบคุมของเส้นโค้ง besel ที่ดึงวงรีมีดังนี้:
จุดควบคุม Bessel x = (ความกว้างของวงรี/0.75)/2 สะท้อนให้เห็นในรหัสแล้ว
คุณสามารถทดสอบสี่วิธีข้างต้นเพื่อวาดวงรี
หากคุณพบวิธีที่ง่ายขึ้นโปรดแบ่งปันและพูดคุย