简体   繁体   English

如何使用 javascript canvas 将圆形变为矩形

[英]how to make circle to rectangle using javascript canvas

hi guys i'm starting to learn about canvas using javascript.大家好,我开始使用 javascript 了解 canvas。 I have a question if there is any way to transform a circle to rectangle smoothly我有一个问题,是否有任何方法可以将圆形平滑地转换为矩形

something like this??像这样的东西??1

Morphing shapes.变形形状。

The easiest method is to create two reference shapes and tween between the two shapes.最简单的方法是创建两个参考形状并在两个形状之间进行补间。

As a rectangle and circle are created from lines, and arcs respectively it is not easy to create a square from circles.由于矩形和圆形分别由直线和圆弧创建,因此从圆形创建正方形并不容易。 However you can create a circle from many small lines.但是,您可以从许多小线创建一个圆圈。

The example/demo snippet implements the following methods示例/演示片段实现了以下方法

Tweening补间

Tweening is a method of interpolating between two states.补间是一种在两个状态之间进行插值的方法。

The amount that we shift between the two states is defined by a unit value (a value from 0 to 1).我们在两种状态之间转换的量由一个单位值(从 0 到 1 的值)定义。

If the value is 0 the we are in the start state, if 1 then in the end state, values between are a fraction between the two end states.如果值为 0,则我们在开始 state,如果为 1,则在结束 state 之间的值是两个结束状态之间的分数。

The function that does the Tweening is called tweenPaths(ctx, pathA, pathB, pod)进行 Tweening 的 function 称为tweenPaths(ctx, pathA, pathB, pod)

Create the shapes创建形状

To get the correct look the points on start shape must be as close as possible to the points on the end shape.为了获得正确的外观,开始形状上的点必须尽可能靠近结束形状上的点。 You also need to consider things like corners making sure you have a point on both shapes that will define a corner on one of the shapes.您还需要考虑诸如角之类的事情,确保您在两个形状上都有一个点,它将在其中一个形状上定义一个角。

The circle圆圈

The function circle(r, points) creates a circle out of a set of line segments. function circle(r, points)从一组线段中创建一个圆。 r is radius and points is the number of points r是半径, points是点数

The square广场

The function square(size, points) starts by calculating a point on the circle and then pushes that point out from the center to where it would be on the square. function square(size, points)首先计算圆上的一个点,然后将该点从中心推到它在正方形上的位置。

size is the width and height and points is the number of points size是宽度和高度, points是点数

Points, Corners, position点、角、position

Note that both circle and square MUST have the same number of points.请注意,圆形和方形必须具有相同的点数。 To get the corners of the square the number of points needs to be divisible by 4为了得到正方形的角点的数量需要被 4 整除

Note both shapes are centered on coordinate 0, 0. To draw at a specific point the 2D context transform sets the center (last two values).请注意,这两个形状都以坐标 0、0 为中心。要在特定点绘制,2D 上下文变换会设置中心(最后两个值)。

ctx.setTransform(1,0,0,1, ctx.canvas.width * 0.5, ctx.canvas.height * 0.5);

In this case that is the center of the canvas.在这种情况下,它是 canvas 的中心。

Example例子

The code below tweens between a circle and square.下面的代码在圆形和方形之间进行补间。

The function eCurve eases in and out the tweening value. function eCurve入和缓出补间值。

To animate the process the example uses requestAnimationFrame to control the timing and rendering of the animation为了使流程动画化,该示例使用requestAnimationFrame来控制 animation 的时间和渲染

 function circle(r, points) { var i = 0; const path = []; while (i < points) { const a = (i++ / points) * Math.PI * 2; path.push({x: Math.cos(a) * r, y: Math.sin(a) * r}); } return path; } function square(size, points) { var i = 0; const path = []; while (i < points) { const a = (i++ / points) * Math.PI * 2; const x = Math.cos(a), y = Math.sin(a); const l = Math.abs(x) - Math.abs(y) > 0? (size * 0.5) / x * Math.sign(x): (size * 0.5) / y * Math.sign(y); path.push({x: x * l, y: y * l}) } return path; } function tweenPaths(ctx, pathA, pathB, pos) { // pos = 0 PathA, pos = 1 PathB var i = 0; ctx.beginPath(); while (i < pathA.length) { const A = pathA[i]; const B = pathB[i]; const x = (Bx - Ax) * pos + Ax; const y = (By - Ay) * pos + Ay; ctx.lineTo(x, y); i++; } ctx.closePath(); ctx.stroke(); } const eCurve = (v, p = 2, vp) => v < 0? 0: v > 1? 1: (vp = v ** p) / (vp + (1 - v) ** p); const ctx = canvas.getContext("2d"); const size = Math.min(ctx.canvas.width, ctx.canvas.height) * 0.4; const cir = circle(size, 200); const sqr = square(size * 2, 200); var tween = 0; var tweenDir = 0.01; requestAnimationFrame(animate); function animate() { ctx.setTransform(1,0,0,1,0,0); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); tween += tweenDir; if (tweenDir < 0 && tween <= 0) { tweenDir = -tweenDir; tween = 0; } else if (tweenDir > 0 && tween >= 1) { tweenDir = -tweenDir; tween = 1; } ctx.lineWidth = 4; ctx.setTransform(1,0,0,1, ctx.canvas.width * 0.5, ctx.canvas.height * 0.5); tweenPaths(ctx, cir, sqr, eCurve(tween)); requestAnimationFrame(animate); }
 <canvas id="canvas" width="200" height="200"></canvas>

The Canvas API allows the use of path-functions similar to SVG's <path> d -attribute . Canvas API允许使用类似于SVG 的<path> d -attribute路径功能
If you have ever worked with SVG and CSS, then transforming a square to a circle seems like an easy task:如果您曾经使用过 SVG 和 CSS,那么将正方形转换为圆形似乎很容易:

 svg { width: 100px; height: 100px; }
 <svg viewBox="0 0 100 100" xmlns="http://w3.org/2000/svg" fill="transparent" stroke="black"> <rect x="5" y="5" width="90" height="90" rx="0"> <animate attributeName="rx" values="0;45;0" dur="2s" repeatCount="indefinite" /> </rect> </svg>

Here, we have a <rect> -element, whose rx -attribute is animated, from 0 to half its size.在这里,我们有一个<rect>元素,它的rx属性是动画的,从 0 到它的一半大小。 This results in a form, that looks like its transforming from a circle to a square.这会产生一种形式,看起来像是从圆形转变为方形。

As any form is made up from lines, curves and other geometric forms, it can be modelled using SVG's <path> -element and its d -attribute.由于任何形式都是由直线、曲线和其他几何 forms 组成的,因此可以使用 SVG 的<path>元素及其d属性对其进行建模。

The Canvas API includes a class called Path2D , which behaves exactly like SVG's <path> -element, just purpose-built for the canvas. The Canvas API includes a class called Path2D , which behaves exactly like SVG's <path> -element, just purpose-built for the canvas.
The Canvas API also includes path-functions to programmatically define the path to draw/fill. Canvas API 还包括路径函数,用于以编程方式定义绘制/填充路径。

Either Path2D or the path-functions may be used to achieve a transforming square-to-circle form. Path2D或路径函数都可用于实现方形到圆形的转换。

Example例子

As we are going to implement it using JavaScript, we can define our own custom animation-curve, here in calcMutliplier() .因为我们将使用 JavaScript 来实现它,所以我们可以在calcMutliplier()中定义我们自己的自定义动画曲线。

The path to draw is defined in render() .绘制路径在render()中定义。

The animation is depending on how much time has passed. animation 取决于经过了多少时间。 The objects are informed of passed time by calling update() with dt as its argument, containing the passed time in milliseconds.通过以dt作为参数调用update()来通知对象经过的时间,其中包含经过的时间(以毫秒为单位)。

 const canvas = document.querySelector('canvas'); const ctx = canvas.getContext('2d'); class SquareCircleForm { x; y; size; cycleLength; progress = 0; constructor(x = 0, y = 0, size = 0, cycleLength = 0) { this.x = x; this.y = y; this.size = size; this.cycleLength = cycleLength; } calcMultiplier() { return Math.abs((this.progress / this.cycleLength) * 2 - 1); } update(dt) { this.progress = (this.progress + dt) % this.cycleLength; } render(ctx) { const mult = this.calcMultiplier(); ctx.save(); ctx.strokeStyle = 'black'; ctx.translate(this.x, this.y); ctx.beginPath(); ctx.moveTo(0, -this.size / 2); // Start point ctx.lineTo(this.size / 2 * mult, -this.size / 2); // Top edge - right half ctx.arcTo( // Top-right corner this.size / 2, -this.size / 2, this.size / 2, this.size / 2, this.size / 2 * (1 - mult) ); ctx.lineTo(this.size / 2, this.size / 2 * mult); // Right edge ctx.arcTo( // Bottom-right corner this.size / 2, this.size / 2, -this.size / 2, this.size / 2, this.size / 2 * (1 - mult) ); ctx.lineTo(-this.size / 2 * mult, this.size / 2); // Bottom edge ctx.arcTo( // Bottom-left corner -this.size / 2, this.size / 2, -this.size / 2, -this.size / 2, this.size / 2 * (1 - mult) ); ctx.lineTo(-this.size / 2, -this.size / 2 * mult); // Left edge ctx.arcTo( // Top-left corner -this.size / 2, -this.size / 2, this.size / 2, -this.size / 2, this.size / 2 * (1 - mult) ); // Top edge - left half not needed; `ctx.closePath()` connects start with end ctx.closePath(); ctx.stroke(); ctx.restore(); } } class SquareCircleForm2 extends SquareCircleForm { constructor(x, y, size, cycleLength) { super(x, y, size, cycleLength); this.progress = this.cycleLength / 4; } calcMultiplier() { return Math.sin(2 * Math.PI * this.progress / this.cycleLength) / 2 + 0.5; } } const forms = [] // Using linear transformation forms.push(new SquareCircleForm(canvas.width / 4, canvas.height / 2, 100, 2000)); // Using sine-wave transformation forms.push(new SquareCircleForm2(canvas.width * 3 / 4, canvas.height / 2, 100, 2000)); const framesPerSecond = 30; let start = Date.now(); setInterval(() => { const dt = Date.now() - start; // Reset canvas ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); // Update and draw forms forms.forEach(f => f.update(dt)); forms.forEach(f => f.render(ctx)); start = Date.now(); }, 1000 / framesPerSecond);
 canvas {border: 1px solid black}
 <canvas></canvas>

Here, the left form animates linearly, and the right by sine-wave.在这里,左边的形式是线性动画,右边是正弦波。

End note尾注

This answer is posted to offer another way of implementing your desired result.发布此答案是为了提供另一种实现所需结果的方法。
However, I think Blindman67's implementation is, for most cases, better suited.但是,我认为Blindman67 的实现在大多数情况下更适合。

His example animates differently from mine, which is due to him positioning the square-points on the radial lines.他的示例与我的示例动画不同,这是由于他将正方形点定位在径向线上。

To achieve the same effect as in my example, you would have to create forms of n points, with n being divisible by 12. Then, you would have to place n / 4 points in each of the corners, in the same direction in which you placed the circle-points, meaning clock- or counter-clockwise.为了达到与我的示例相同的效果,您必须创建n个点的 forms,其中n可以被 12 整除。然后,您必须在每个角上放置n / 4个点,方向相同你放置了圆点,意思是时钟或逆时针。
Then, it would be a simple matter of just using his tweenPaths() function.然后,只需使用他的tweenPaths() function 就很简单了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM