javascript
keras 香草编码器_完善纯香草javascript中的拖放
keras 香草編碼器
Drag-and-drop functionality is the bread and butter of a modern web UX. It’s an aspect of the API, part of the HTML standard. Lots of UI libraries provide it out of box.
拖放功能是現代Web UX的基礎。 這是API的一個方面 ,是HTML標準的一部分。 許多UI庫都是開箱即用的。
Sometimes, though, we have to deal with situations where neither the standard API nor the libraries can help, and we have to implement the functionality ourselves. In this article we’ll do exactly that—we’ll implement:
但是,有時候,我們必須處理標準API和庫都無法提供幫助的情況,而我們必須自己實現功能。 在本文中,我們將完全做到這一點-我們將實現:
- Cross-input (with blended mouse and touch-events support) 交叉輸入(具有混合的鼠標和觸摸事件支持)
- Cross-browser friendly 跨瀏覽器友好
- Performant (60 FPS) 性能(60 FPS)
- Efficient (no wasted JavaScript processing) 高效(不浪費JavaScript處理)
1.先決條件 (1. Prerequisites)
For the sake of this article, we’re going to be dragging a few black square boxes (divs) around a container (div) element in a simple HTML page:
為了本文的目的,我們將在一個簡單HTML頁面中的容器(div)元素周圍拖動幾個黑色方框(divs):
<div class="container"><div style="top: 100px; left: 100px" class="box"></div><div style="top: 200px; left: 200px" class="box"></div><div style="top: 300px; left: 300px" class="box"></div><div style="top: 400px; left: 400px" class="box"></div> </div>.container {width: 600px;height: 600px;background-color: darkgrey;touch-action: none; } .box {position: absolute;width: 100px;height: 100px;background-color: black; }Nothing fancy here, but note we’re applying the touch-action: none; CSS rule to prevent any browser level manipulations (such as scroll or zoom) for events inside the container.
這里沒什么好看的,但請注意,我們正在應用touch-action: none; CSS規則 ,可防止對容器內的事件進行任何瀏覽器級別的操作(例如,滾動或縮放)。
2.添加輸入監聽器 (2. Adding Input Listeners)
Pointer events provide the blender supporting both input types指針事件提供了支持兩種輸入類型的混合器Since we want to support cross-input, we could write some detection mechanism for whether the user is using touch input or mouse input, but in the modern world, the user might be using both input types at the same time (touchscreen laptop, anyone?).
因為我們要支持交叉輸入,所以我們可以編寫一些檢測機制來確定用戶是使用觸摸輸入還是鼠標輸入,但是在現代世界中,用戶可能同時使用兩種輸入類型 (觸摸屏筆記本電腦,有人嗎?)。
Hence, we should aim to support both. One way would be to attach both types of listeners, but a better way is to leverage the Pointer Events API — the blender of two input types that nowadays has decent support by all of the major browsers.
因此,我們應該努力支持兩者。 一種方法是連接兩種類型的偵聽器,但更好的方法是利用Pointer Events API-兩種輸入類型的混合器,如今,所有主要瀏覽器都提供了不錯的支持。
Our drag-and-drop user experience can be mapped this way:
可以通過以下方式映射我們的拖放式用戶體驗:
User points at a box, presses the mouse button/screen (pointerdown)
用戶指向一個框,按下鼠標按鈕/屏幕( pointerdown )
User drags the box by moving the mouse/their finger across the screen (pointermove)
用戶通過在屏幕上移動鼠標/手指來拖動框( pointermove )
User drops the box by releasing the mouse/their finger from the screen (pointerup/pointercancel*)
用戶通過從屏幕上釋放鼠標/手指來pointerup pointercancel ( pointerup / pointercancel *)
*Touch devices feature an additional touchcancel event — for the cases when the touch event gets disrupted (for example, when there are too many touch points on the screen).
*觸摸設備還具有一個附加的touchcancel事件-在觸摸事件中斷的情況下(例如,當屏幕上的觸摸點過多時)。
So let’s write some JavaScript and add our event listeners:
因此,讓我們編寫一些JavaScript并添加事件監聽器:
const container = document.querySelector('.container'); container.addEventListener('pointerdown', userPressed, { passive: true });function userPressed(event) {element = event.target;if (element.classList.contains('box')) {console.log("pick up!")container.addEventListener('pointermove', userMoved, { passive: true });container.addEventListener('pointerup', userReleased, { passive: true });container.addEventListener('pointercancel', userReleased, { passive: true });}; };function userMoved(event) {console.log("dragging!") };function userReleased(event) {console.log("dropped!")container.removeEventListener('pointermove', userMoved);container.removeEventListener('pointerup', userReleased);container.removeEventListener('pointercancel', userReleased); };一些注意事項 (A few notes)
We’re only attaching the pointerdown event to the container and adding/removing other listeners dynamically to prevent event pollution — we’re using listeners on an as-needed basis, keeping things tight.
我們只是將pointerdown事件附加到容器上,并動態添加/刪除其他偵聽器以防止事件污染—我們在需要時使用偵聽器,以保持緊密狀態。
The events are attached to the container, as we’re using event delegation.
事件被附加到容器,因為我們正在使用事件委托 。
We’re also using the passive flag to ensure our events don’t interfere with our scrolling events.
我們還使用passive標志來確保事件不會干擾滾動事件。
3.使盒子移動 (3. Making Boxes Move)
Our boxes feature absolute positioning and rely on left/top properties to be positioned inside of the container. Let’s add functionality to actually move the boxes around:
我們的盒子具有absolute定位功能,并依靠left / top屬性在容器內部定位。 讓我們添加功能以實際移動框:
const container = document.querySelector('.container'); container.addEventListener('pointerdown', userPressed, { passive: true });var element, bbox, startX, startY;function userPressed(event) {element = event.target;if (element.classList.contains('box')) {startX = event.clientX;startY = event.clientY;bbox = element.getBoundingClientRect();container.addEventListener('pointermove', userMoved, { passive: true });container.addEventListener('pointerup', userReleased, { passive: true });container.addEventListener('pointercancel', userReleased, { passive: true });}; };function userMoved(event) {let deltaX = event.clientX - startX;let deltaY = event.clientY - startY;element.style.left = bbox.left + deltaX + "px";element.style.top = bbox.top + deltaY + "px"; };function userReleased(event) {container.removeEventListener('pointermove', userMoved);container.removeEventListener('pointerup', userReleased);container.removeEventListener('pointercancel', userReleased); };There are now more things going on here:
現在,這里還有更多事情要做:
Inside the userPressed event handler, we capture the coordinates of the starting point for the movement as well as the box element’s BoundingClientRect; both will help to calculate the amount of movement we need to apply to the box based on the pointer’s distance covered during the move.
在userPressed事件處理程序內部,我們捕獲了運動起點的坐標以及box元素的BoundingClientRect ; 兩者都將有助于根據移動過程中指針所經過的距離來計算我們需要應用于框的移動量。
Inside the userMoved handler, we leverage startX and startY data to calculate the delta for which the box should visually move in relation to its previous position.
在userMoved處理程序內部,我們利用startX和startY數據來計算框應相對于其先前位置在視覺上移動的delta 。
Now the user can, in fact, move the boxes and do so using touch or mouse input.
現在,用戶實際上可以移動盒子并使用觸摸或鼠標輸入進行移動。
4.調優性能 (4. Tuning Performance)
There are a few known approaches we can take to improve performance. In this simplistic case, we don’t perform any heavy calculations; hence, even as is the performance looks fine. But we can consider the following options:
我們可以采取幾種已知的方法來提高性能。 在這種簡單的情況下,我們不執行任何繁重的計算; 因此,即使性能看起來也不錯。 但是我們可以考慮以下選擇:
'transform3d' (‘transform3d’)
This trick might be out of date nowadays, as modern browsers learned to optimize for layout changes, but it’s still useful to know that instead of directly modifying our box’s left and top positions, we could apply transform during pointermove and only apply actual style changes on pointerup:
由于現代瀏覽器已學會優化布局更改,因此這種技巧可能已不合時宜,但了解而不是直接修改框的left和top位置,我們可以在pointermove期間應用transform并僅對pointerup :
const container = document.querySelector('.container'); container.addEventListener('pointerdown', userPressed, { passive: true });var element, bbox, startX, startY, deltaX, deltaY;function userPressed(event) {element = event.target;if (element.classList.contains('box')) {startX = event.clientX;startY = event.clientY;bbox = element.getBoundingClientRect();container.addEventListener('pointermove', userMoved, { passive: true });container.addEventListener('pointerup', userReleased, { passive: true });container.addEventListener('pointercancel', userReleased, { passive: true });}; };function userMoved(event) {deltaX = event.clientX - startX;deltaY = event.clientY - startY;element.style.transform = "translate3d("+deltaX+"px,"+deltaY+"px, 0px)"; };function userReleased(event) {container.removeEventListener('pointermove', userMoved);container.removeEventListener('pointerup', userReleased);container.removeEventListener('pointercancel', userReleased);element.style.left = bbox.left + deltaX + "px";element.style.top = bbox.top + deltaY + "px";element.style.transform = "translate3d(0px,0px,0px)"; };'RequestAnimationFrame' (‘RequestAnimationFrame’)
We can also achieve smoother animation while dragging (~60 FPS) through the use of the requestAnimationFrame API:
通過使用requestAnimationFrame API,我們還可以在拖動(?60 FPS)的同時實現更流暢的動畫:
const container = document.querySelector('.container'); container.addEventListener('pointerdown', userPressed, { passive: true });var element, bbox, startX, startY, deltaX, deltaY, raf;function userPressed(event) {element = event.target;if (element.classList.contains('box')) {startX = event.clientX;startY = event.clientY;bbox = element.getBoundingClientRect();container.addEventListener('pointermove', userMoved, { passive: true });container.addEventListener('pointerup', userReleased, { passive: true });container.addEventListener('pointercancel', userReleased, { passive: true });}; };function userMoved(event) {// if no previous request for animation frame - we allow js to proccess 'move' event:if (!raf) {deltaX = event.clientX - startX;deltaY = event.clientY - startY;raf = requestAnimationFrame(userMovedRaf);} };function userMovedRaf() {element.style.transform = "translate3d("+deltaX+"px,"+deltaY+"px, 0px)";// once the paint job is done we 'release' animation frame variable to allow next paint job:raf = null; };function userReleased(event) {container.removeEventListener('pointermove', userMoved);container.removeEventListener('pointerup', userReleased);container.removeEventListener('pointercancel', userReleased);// if animation frame was scheduled but the user already stopped interaction - we cancel the scheduled frame:if (raf) {cancelAnimationFrame(raf);raf = null;};element.style.left = bbox.left + deltaX + "px";element.style.top = bbox.top + deltaY + "px";element.style.transform = "translate3d(0px,0px,0px)"; };這里有幾點注意事項 (A few notes here)
We added the raf variable to helps us debounce pointermove events with help from the requestAnimationFrame timer, as we don’t want JavaScript to perform calculations that won’t be used since the animation frame was already scheduled and hasn’t yet completed.
我們添加了raf變量,以幫助我們在requestAnimationFrame計時器的幫助下對pointermove事件進行反跳,因為我們不希望JavaScript執行由于已經安排好動畫框架且尚未完成而不會使用的計算。
We made sure the pointermove event, which fires many times per second, is lightweight and only contains the logic necessary to obtain the coordinates information and triggers the style change.
我們確保了pointermove觸發多次的pointermove事件是輕量級的,并且僅包含獲取坐標信息并觸發樣式更改所需的邏輯。
3. CSS規則:“將改變”和“包含” (3. CSS Rules: ‘will-change’ and ‘contain’)
In addition to the above, there are CSS rules that you could explore to tune performance further.
除了上述內容,您還可以探索CSS規則來進一步調整性能。
The will-change CSS rule indicates to your browser the element’s attributes are expected to change and allows the browser to perform additional optimizations. But as the documentation says — use it with caution:
的 will-change CSS規則指示瀏覽器元素的屬性都有望改變,并允許瀏覽器執行額外的優化。 但正如文檔所述,請謹慎使用:
“Important: will-change is intended to be used as a last resort, in order to try to deal with existing performance problems. It should not be used to anticipate performance problems.”
“重要: will-change是不得已的手段,目的是試圖解決現有的績效問題。 它不應該被用來預測性能問題。”
.box {position: absolute;width: 100px;height: 100px;background-color: black;will-change: transform, left, top;contain: layout; }The contain CSS rule isn’t fully supported by all browsers yet but can help indicate that a particular DOM’s element style or layout changes won’t affect another page’s elements and thus won’t trigger potential expensive reflows.
在 contain CSS規則沒有完全被所有的瀏覽器,但還可以幫助支持表示一個特定的DOM的元素風格或布局的變化不會影響其他頁面的元素,因而不會觸發潛在的昂貴的回流。
It isn’t really adding any value in our simplistic case (given that our boxes are positioned as absolute, but it’s a useful trick in more complex UI and dragging situations. You can watch this amazing video for more details.
在我們的簡單案例中,它并沒有真正增加任何價值(假設我們的盒子定位為absolute ,但這在更復雜的UI和拖動情況下是一個有用的技巧。您可以觀看此精彩視頻 ,了解更多詳細信息。
結論 (Conclusion)
We went through the basics of how drag-and-drop functionality can be implemented in pure JavaScript, and we outlined a few techniques and directions for performance optimization. The main things to remember:
我們介紹了如何在純JavaScript中實現拖放功能的基礎知識,并概述了一些性能優化的技術和方向。 要記住的主要事項:
- Leverage blended input events to ensure hybrid devices are covered. 利用混合輸入事件來確保涵蓋混合設備。
Attach/detach move/up/cancel events dynamically.
動態附加/分離move / up / cancel事件。
Do as much of the heavy weight lifting upfront or inside the down and up handlers, but keep the move handler lightweight.
做盡可能多的重型舉重前期的或內部down和up處理程序,但保持move處理器輕巧。
Leverage requestAnimationFrame for debouncing and smooth animation while dragging.
利用requestAnimationFrame在拖動時進行反跳和平滑動畫處理。
- Explore CSS rules that can help gain additional performance. 探索有助于提高性能CSS規則。
Full implementation here:
完整的實現在這里:
演示地址
Thank you for reading the article, and happy coding!
感謝您閱讀本文,并祝您編程愉快!
翻譯自: https://medium.com/better-programming/perfecting-drag-and-drop-in-pure-vanilla-javascript-a761184b797a
keras 香草編碼器
總結
以上是生活随笔為你收集整理的keras 香草编码器_完善纯香草javascript中的拖放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: go 捕获数据库新增数据_更改数据捕获的
- 下一篇: shields 徽标_所有徽标看起来都一