This snippet lets you add playful, interactive drag behavior to almost any element on your site using GSAP. Just add a simple attribute and the item instantly becomes draggable, with no extra setup required.
It’s perfect for creating effects like dragging product badges, rearranging cards inside a section, moving notes on a board-style layout, or letting users explore content by dragging elements around the screen.
When you grab an item, it reacts with a subtle animation so it feels “picked up,” and when you release it, it returns to its original look.
Inertia is built in, so movement feels smooth and natural instead of stiff.
Copy & paste the scripts before the </body> tag of your project. If you added them before for another setup, skip this step.
<!-- GSAP -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<!-- Drag support -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/Draggable.min.js"></script>
<!-- Smooth inertia -->
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/InertiaPlugin.min.js"></script>Right-click in Elementor, choose “Paste from another site,” and while the popup is open, press cmd/ctrl + v to insert the layout.
Place the code in an HTML widget or add it through Elementor → Custom Code (before the closing </body> tag) either globally or only on selected pages.
Paste the code through the page or site settings, or add it via Elementor → Custom Code (before </body>) for broader use.
[ou-gsap-drag="item"] {
transition: none !important;
}Paste the script through Elementor → Custom Code (set to load after </body>) for site-wide or page-specific loading.
gsap.registerPlugin(Draggable, InertiaPlugin);
document.addEventListener("DOMContentLoaded", () => {
if (window.elementorFrontend && elementorFrontend.isEditMode()) return;
const ATTR = {
item: 'ou-gsap-drag="item"',
wrapper: 'ou-gsap-drag="wrapper"',
scale: 'ou-gsap-drag-scale',
rotate: 'ou-gsap-drag-rotate',
inertia: 'ou-gsap-drag-inertia',
resistance: 'ou-gsap-drag-resistance'
};
document.querySelectorAll(`[${ATTR.item}]`).forEach(item => {
const bounds =
item.closest(`[${ATTR.wrapper}]`) || window;
const cfg = {
scale: parseFloat(item.getAttribute(ATTR.scale)) || 1.15,
rotate: parseFloat(item.getAttribute(ATTR.rotate)) || 25,
inertia: item.getAttribute(ATTR.inertia) !== "false",
resistance: parseFloat(item.getAttribute(ATTR.resistance)) || 0.1
};
const grabTl = gsap.timeline({ paused: true })
.set(item, { cursor: "grabbing" })
.to(item, {
scale: cfg.scale,
rotation: gsap.utils.random(-cfg.rotate, cfg.rotate),
duration: 0.2,
ease: "power2.out"
});
Draggable.create(item, {
bounds,
inertia: cfg.inertia,
dragResistance: cfg.resistance,
onPress() {
grabTl.play();
},
onRelease() {
grabTl.reverse().eventCallback("onReverseComplete", () => {
gsap.set(item, { cursor: "grab" });
});
},
onDragStart() {
this.target.style.zIndex = Date.now();
}
});
});
});Place the PHP snippet inside your theme’s functions.php or using any code snippet to enable logic.
All behavior is controlled through ou-gsap-drag-* attributes, allowing per-element customization without modifying the JavaScript.
Use ou-gsap-drag="item" on any element you want to make draggable. Any element (button, image, card, icon, or any other element) with this attribute will be initialized and become draggable.
If you want to add a limit to where an item can be dragged, place it inside a container that has the attribute ou-gsap-drag|wrapper. That container becomes the allowed movement area, so the item will stay inside it and cannot be dragged outside of that wrapper.
If no wrapper is found, the window is used as the boundary.
Use ou-gsap-drag-scale to control how much the element scales when it is grabbed. The value must be a number such as 1.15. If the attribute is not set, the default value of 1.15 is used.
Use ou-gsap-drag-rotate to control the maximum random rotation applied during grab. The value must be a number such as 25, meaning the element can rotate between -25 and +25. If the attribute is not set, the default value of 25 is used.
Use ou-gsap-drag-inertia to control inertia behavior. Set the value to false to disable inertia for a specific element. If the attribute is missing, inertia remains enabled by default.
Use ou-gsap-drag-resistance to control how resistant the element feels while dragging.
The value must be a number between 0 and 1. Higher values feel heavier and slower, while lower values feel lighter and faster.
If this attribute is not provided, the default value used by the script is 0.1.
The grab and release animations use fixed timing and easing defined in the script.
The scale and rotation animation runs for 0.2 seconds using the power2.out easing curve. On release, the animation reverses using the same duration and easing. These values are global and apply to all draggable items.
To change the feel of all drags, adjust the duration or easing directly in the JavaScript.