autoScroll
autoScroll : Number
How fast to scroll the container element when autoScroll is true.
Details
Number - To enable auto-scrolling when a Draggable is dragged within 40px of an edge of a scrollable container, set autoScroll to a non-zero value, where 1 is normal speed, 2 is double-speed, etc. (you can use any number). For a more intuitive or natural feel, it will scroll faster as the mouse/touch gets closer to the edge. The default value is 0 (no auto-scrolling).
<h1>Draggable autoScroll</h1>
<p>As of version 0.12.0, Draggable has auto-scrolling capabilities! Add <code>autoScroll:1</code> to the config object for normal-speed scrolling, or <code>autoScroll:2</code> would be twice as fast, etc. When your mouse moves within 40px of the edge of a scrollable container, it will start scrolling. The closer you get to the edge, the faster it will scroll. Try it out below...</p>
<div class="wrapper">
<div id="container">
<div class="box green" id="box">Drag me</div>
</div>
</div>
<div class="controls">
<ul>
<li class="controlsTitle">Options</li>
<li>
<label><input type="checkbox" name="snap" id="snap" value="1" /> Snap end position to grid</label>
</li>
<li>
<label><input type="checkbox" name="liveSnap" id="liveSnap" value="1" /> Live snap</label>
</li>
</ul>
</div>/* GSAP brand: fonts, type sizes, colour vars, buttons and form controls.
Element selectors only, so a demo's own class-based styles still win. */
/* Fonts */
@font-face {
font-display: swap;
font-family: Mori;
font-weight: 400;
src: url(/fonts/Mori/PPMori-Regular.woff) format("woff");
}
@font-face {
font-display: swap;
font-family: Mori;
font-weight: 600;
src: url(/fonts/Mori/PPMori-SemiBold.woff) format("woff");
}
@font-face {
font-display: swap;
font-family: "Fraktion Mono";
font-weight: 400;
src: url(/fonts/Fraktion/PPFraktionMono-Bold.woff) format("woff");
}
/* Colours */
:root {
--color-shockingly-green: #0ae448;
--color-just-black: #0e100f;
--color-surface-white: #fffce1;
--color-pink: #fec5fb;
--color-shockingly-pink: #f100cb;
--color-orangey: #ff8709;
--color-lilac: #9d95ff;
--color-lt-green: #abff84;
--color-blue: #00bae2;
--color-grey: #191919;
--color-surface75: #bbbaa6;
--color-surface50: #7c7c6f;
--color-surface25: #42433d;
--gradient-macha: linear-gradient(114.41deg, var(--color-shockingly-green) 20.74%, var(--color-lt-green) 65.5%);
--gradient-orange-crush: linear-gradient(111.45deg, var(--color-orangey) 19.42%, #f7bdf8 73.08%);
--gradient-lipstick: linear-gradient(165.72deg, #f7bdf8 21.15%, #cd237f 81.93%);
--gradient-purple-haze: linear-gradient(153.58deg, #f7bdf8 32.25%, #2f3cc0 92.68%);
--gradient-skyfall: linear-gradient(131.77deg, #0a157a 30.82%, #15bfe4 81.82%);
--gradient-emerald-city: linear-gradient(166.9deg, var(--color-shockingly-green) 53.19%, #0085d0 107.69%);
--gradient-summer-fair: linear-gradient(144.02deg, var(--color-blue) 4.56%, var(--color-pink) 72.98%);
/* Short names many demos use */
--dark: var(--color-just-black);
--light: var(--color-surface-white);
--mid: var(--color-surface50);
--grey: var(--color-grey);
--gray: var(--color-grey);
--green: var(--color-shockingly-green);
--green-light: var(--color-lt-green);
--blue: var(--color-blue);
--purple: var(--color-lilac);
--pink: var(--color-pink);
--orange: var(--color-orangey);
--red: #cd237f;
/* Form controls */
--field-bg: #1c1d1f;
--field-border: var(--color-surface25);
--field-radius: 8px;
--focus-ring: 0 0 0 3px rgba(10, 228, 72, 0.35);
accent-color: var(--color-shockingly-green);
color-scheme: dark;
}
/* Type */
* { box-sizing: border-box; }
body {
margin: 0;
background-color: var(--color-just-black);
color: var(--color-surface75);
font-family: Mori, system-ui, sans-serif;
font-size: clamp(1rem, 0.95rem + 0.25vw, 1.125rem);
line-height: 1.45;
-webkit-font-smoothing: antialiased;
}
h1, h2, h3, h4 {
margin: 0.5em 0;
color: var(--color-surface-white);
font-weight: 400;
line-height: 1.2;
}
h1 { font-size: clamp(2rem, 1.6rem + 2vw, 2.875rem); }
h2 { font-size: clamp(1.625rem, 1.4rem + 1.2vw, 2.2rem); }
h3 { font-size: clamp(1.375rem, 1.25rem + 0.6vw, 1.75rem); }
h4 { font-size: clamp(1.125rem, 1rem + 0.5vw, 1.4rem); }
a { color: inherit; text-underline-offset: 0.15em; }
code, pre, kbd { font-family: "Fraktion Mono", ui-monospace, Menlo, monospace; }
/* Buttons */
button,
input[type="button"],
input[type="submit"],
input[type="reset"] {
display: inline-flex;
align-items: center;
justify-content: center;
gap: 0.5em;
min-height: 2.5rem;
padding: 0 1.25rem;
border: 2px solid var(--color-surface75);
border-radius: 99px;
background: var(--color-just-black);
color: var(--color-surface75);
font: 600 0.9rem/1 Mori, system-ui, sans-serif;
cursor: pointer;
transition: border-color 0.2s, color 0.2s, background-color 0.2s;
}
button:hover,
input[type="button"]:hover,
input[type="submit"]:hover,
input[type="reset"]:hover {
border-color: var(--color-surface-white);
color: var(--color-surface-white);
}
button:disabled,
input:disabled { opacity: 0.4; cursor: not-allowed; }
/* Form controls */
label { color: var(--color-surface75); font-size: 0.9rem; }
input[type="text"],
input[type="number"],
input[type="email"],
input[type="search"],
input[type="url"],
input:not([type]),
select,
textarea {
min-height: 2.5rem;
padding: 0 0.75rem;
border: 1px solid var(--field-border);
border-radius: var(--field-radius);
background: var(--field-bg);
color: var(--color-surface-white);
font: 400 0.9rem/1.2 Mori, system-ui, sans-serif;
transition: border-color 0.2s, box-shadow 0.2s;
}
textarea { padding: 0.6rem 0.75rem; line-height: 1.45; }
input::placeholder,
textarea::placeholder { color: var(--color-surface50); }
input[type="checkbox"],
input[type="radio"] {
width: 1.1em;
height: 1.1em;
margin: 0 0.4em 0 0;
vertical-align: -0.15em;
}
input[type="range"] { width: min(100%, 18rem); }
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: none;
border-color: var(--color-shockingly-green);
box-shadow: var(--focus-ring);
}
body {
padding: 1rem;
}
body {
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
flex-direction: column;
}
.wrapper {
width: 60vw;
height: 1020px;
overflow: scroll;
}
#container {
height: 801px;
overflow: visible;
padding: 0;
position: relative;
}
.box {
text-align: center;
font-size: 18px;
width: 196px;
line-height: 100px;
color: black;
position: absolute;
top: 0;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
background: var(--gradient-macha);
}
p {
max-width: 70ch;
}
.controls {
background-color: #222;
border: 1px solid #555;
color: #bbb;
font-size: 18px;
margin: 20px 0;
}
.controls ul {
list-style: none;
padding: 0;
margin: 0;
}
.controls li {
display: inline-block;
padding: 8px 0 8px 10px;
margin: 0;
}
.controls input {
vertical-align: middle;
cursor: pointer;
}
.controls .controlsTitle {
border-right: 1px solid #555;
border-bottom: none;
padding-right: 10px;
}// See https://greensock.com/gsap-1-16 for details.
const snapCheckbox = document.getElementById("snap");
const liveSnapCheckbox = document.getElementById("liveSnap");
const container = document.getElementById("container");
const gridWidth = 74;
const gridHeight = 50;
const gridRows = 28;
const gridColumns = 40;
// Create the grid cells
for (let i = 0; i < gridRows * gridColumns; i++) {
const y = Math.floor(i / gridColumns) * gridHeight;
const x = (i * gridWidth) % (gridColumns * gridWidth);
const cell = document.createElement("div");
cell.style.position = "absolute";
cell.style.border = "1px solid #454545";
cell.style.width = `${gridWidth - 1}px`;
cell.style.height = `${gridHeight - 1}px`;
cell.style.top = `${y}px`;
cell.style.left = `${x}px`;
container.prepend(cell);
}
// Set container and box sizes
gsap.set(container, {
height: gridRows * gridHeight + 1,
width: gridColumns * gridWidth + 1
});
gsap.set("#box", { width: 150, height: 100, lineHeight: "100px" });
function update() {
const snap = snapCheckbox.checked;
const liveSnap = liveSnapCheckbox.checked;
Draggable.create("#box", {
bounds: container,
autoScroll: 1,
edgeResistance: 0.65,
type: "x,y",
throwProps: true,
liveSnap: liveSnap,
onRelease: function () {
this.tween.progress(1);
const wrapper = document.querySelector(".wrapper");
const tBounds = this.target.getBoundingClientRect();
const wBounds = wrapper.getBoundingClientRect();
let wCenter = wBounds.left + wBounds.width / 2;
let tCenter = tBounds.left + tBounds.width / 2;
const scroll = {};
if (tBounds.right > wBounds.right || tBounds.left < wBounds.left) {
scroll.x = wrapper.scrollLeft + (tCenter - wCenter);
}
if (tBounds.bottom > wBounds.bottom || tBounds.top < wBounds.top) {
wCenter = wBounds.top + wBounds.height / 2;
tCenter = tBounds.top + tBounds.height / 2;
scroll.y = wrapper.scrollTop + (tCenter - wCenter);
}
gsap.to(wrapper, this.tween.duration(), { scrollTo: scroll });
this.tween.progress(0);
},
snap: {
x: function (endValue) {
return snap || liveSnap
? Math.round(endValue / gridWidth) * gridWidth
: endValue;
},
y: function (endValue) {
return snap || liveSnap
? Math.round(endValue / gridHeight) * gridHeight
: endValue;
}
}
});
}
function applySnap() {
if (snapCheckbox.checked || liveSnapCheckbox.checked) {
document.querySelectorAll(".box").forEach((element) => {
const transform =
element._gsTransform || gsap.getProperty(element, "x,y");
gsap.to(element, 0.5, {
x: Math.round(transform.x / gridWidth) * gridWidth,
y: Math.round(transform.y / gridHeight) * gridHeight,
delay: 0.1,
ease: Power2.easeInOut
});
});
}
update();
}
// Hook up event listeners
snapCheckbox.addEventListener("change", applySnap);
liveSnapCheckbox.addEventListener("change", applySnap);
// Initial setup
update();