Dragging, Resizing and Rotating HTML elements: Fun with Web Components and Math

栏目: IT技术 · 发布时间: 5年前

内容简介:I was recently faced with the following problem: How do you resize a rotated HTML element by dragging a corner, without the rest of the element moving? This turns out to be non-trivial, but I finally got to write some JavaScript and open my Linear Algebra

March 3, 2020

I was recently faced with the following problem: How do you resize a rotated HTML element by dragging a corner, without the rest of the element moving? This turns out to be non-trivial, but I finally got to write some JavaScript and open my Linear Algebra toolbox.

A bit of background: for the last year, David Dal Busco and I have been developing DeckDeckGo , a tool for creating and sharing presentations. There was one particular feature we wanted: being able to add elements to any slide and be able to drag it, resize it and rotate it. Here it is in action (feel free to use the open source Web Component ):

Dragging, Resizing and Rotating HTML elements: Fun with Web Components and Math

WANTED

10₿

In normal times, David is taking care of the frontend, and yours truly is taking care of the backend. But this time, we had to join forces, and I even had to involve cryptography researcher Bogdan Warinschi (thanks Bogdan). We had a lot of fire power. Nothing is ever easy in the JavaScript world.

The Problem

Say you have an HTML element (like a <div> ). The browser draws a nice rectangle. Then you rotate it. The browser draws a nice, rotated, rectangle. Now you want it to be resized when dragged by a corner. How hard can that be?

We know top and left , which are the CSS attributes for specifying the position of the un-rotated rectangle. We call p the top-left corner of the rectangle. We also know θ , the angle of the rotation. Here we’ll focus on one case: the user drags the bottom-right corner, p . That is, the bottom-right of the rotated rectangle. We can easily recover its position: that’s where the user’s cursor is when they start dragging!

Dragging, Resizing and Rotating HTML elements: Fun with Web Components and Math

Here’s the million dollar question: when the user moves q around, how do we make sure that p (the top-left, rotated corner) stays in place?

Because that, really, is our goal! You might think that for each n pixels that q is moved to the right, we increase the rectangle’s width by n pixels. That would work… if there was no rotation! As soon as the rectangle is rotated, everything gets ugly. One thing in particular that may not be straightforward is that the unrotated’s rectangle top-left corner ( p ) will move as well…

Enter: The Matrix

Dragging, Resizing and Rotating HTML elements: Fun with Web Components and Math
János Pach Warned You

Let’s recap. All CSS values, top , left , θ , width and height are under our control: we can read and write them. Moreover we know where the cursor is when the user starts dragging: that’s q , the corner being dragged.

One other thing we know: p should not move while q is being dragged about. So let’s start by figuring out what the position of p is! The browser knows it (because it is displaying it) but won’t tell us. So we have to compute it ourselves. How?

The browser starts with a rectangle with top-left corner at ( p x , p y ) and then rotates it around the rectangle’s center c . In order for us to compute that, we’ll use the matrix representation of Affine transformations . The beauty of those matrices is that you can compose them: you want to rotate p by angle θ , and then translate it by some v ? Fine! That’s \( t(v) \cdot r(\theta) \cdot p \) (because of boring reason we do this right-to-left), where \( r(\theta) \) is the matrix of rotation by θ (around the origin), and \( t(v) \) is the matrix of translation by v .

If you’re curious, this is what the matrices actually look like:

$t(v) = \begin{bmatrix} 1 & 0 & v_x \\ 0 & 1 & v_y \\ 0 & 0 & 1 \end{bmatrix}$ , $r(\theta) = \begin{bmatrix} \cos(\theta) & - \sin(\theta) & 0 \\ \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix}$

But… that’s the rotation around the origin! We want to be like the browser, we want to rotate around c !!! I learned matrices in vaaaaaaain!

Don’t worry, after 5 years studying in Switzerland’s Federal Institutes of Technology, I had the exact same feeling. Then JavaScript came to the rescue and I can finally use my knowledge of matrices.

Don’t worry, remember how we can compose those transformations? In order to rotate around a point that is not the origin, we just have to pretend that point is actually the origin for the duration of the rotation. Basically, move that point (and everything else) to the origin, rotate, and then move back.

Dragging, Resizing and Rotating HTML elements: Fun with Web Components and Math

That means a rotation around a point c is something like this:

r ( c , θ ) = t ( c ) ⋅ r ( θ ) ⋅ t ( − c )

and the center of the rectangle is simply (left + width/2, top + height/2) . Congrats, we have p , the-point-that-should-never-move!

p ′ = r ( c , θ ) ⋅ p

Rinse, Repeat

Believe it or not, the rest is just a variation on the above. In order to calculate p (which means top and left ) we can simply rotate p back, but this time around the new center of rotation. New? Yes! Because when q was dragged, the center of the rectangle moved!

Dragging, Resizing and Rotating HTML elements: Fun with Web Components and Math

This new center is simply the point halfway between the new, dragged q and the original, never-to-be-moved top-left corner p . Now the new top-left corner that we should give the browser is:

$\begin{bmatrix} left \\ top \\ 1 \end{bmatrix} = p = r(c', - \theta) \cdot p' = r(q' -p', - \theta) \cdot p'$

What’s left to compute? The new width and height. They’re kind of tricky to get in the rotated rectangle, but if we unrotate the resized rectangle, then the width is the horizontal distance between p and q , and the height is the vertical distance between p and q ! We just computed p , so let’s now figure out q (bottom-right corner in the unrotated, resized rectangle):

\( q = r(c’, - \theta) \cdot q’ = r(q’ - p’, - \theta) \cdot q’ \)

$\begin{bmatrix} w \\ h \\ 1 \end{bmatrix} = q - p$

And that’s it. We computed the new top and left values ( p ), which we can specify when redrawing the resized rectangle, alongside its new width and new height.

Where Is The Code?

Deriving the corresponding code is left as an exercise to the reader! Better yet, why don’t you use our open-source Web Component instead ? There’s a lot we didn’t cover in this article, in particular how to handle minimum widths and heights and how to make all corners draggable. Instead I invite you to study DeckDeckGo’s code !


以上所述就是小编给大家介绍的《Dragging, Resizing and Rotating HTML elements: Fun with Web Components and Math》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

马尔可夫链:模型、算法与应用

马尔可夫链:模型、算法与应用

Wai-Ki Ching、Ximin Huang / 陈曦 / 清华大学出版社 / 2015-6 / 39

《马尔可夫链:模型、算法与应用 应用数学译丛》讲述了马尔可夫链模型在排队系统、网页重要性排名、制造系统、再制造系统、库存系统以及金融风险管理等方面的最新应用进展.全书共安排8章内容,第1章介绍马尔可夫链、隐马尔可夫模型和马尔可夫决策过程的基本理论和方法,其余7章分别介绍马尔可夫链模型在不同领域中的应用. 《马尔可夫链:模型、算法与应用 应用数学译丛》可作为自动化、工业工程、统计学、应用数学以及管理......一起来看看 《马尔可夫链:模型、算法与应用》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试