Draggable
Draggable
是一个用于实现拖动效果的 GSAP 插件。
注册插件
import { gsap } from "gsap";
import { Draggable } from "gsap/Draggable";
gsap.registerPlugin(Draggable);
使用插件
Draggable.create(selector, options)
创建一个可拖动的元素。
其中 selector
是可拖动元素的 CSS 选择器或 DOM 元素;options
是一个对象,用于配置拖动行为。
// 使用 Draggable 插件
Draggable.create(".draggable");
// 使用 Draggable 插件,并传入配置项
Draggable.create(".draggable", {
type: "x", // 只允许水平拖动
bounds: ".container", // 设置拖动的边界为 .container 类名的元素
edgeResistance: true, // 当拖到边缘时,会有阻力效果
onDragStart() {}, // 开始拖动时的回调函数
onDragEnd() {}, // 结束拖动时的回调函数
});
常用配置项
activeCursor
activeCursor: String
在用户按下和释放指针/鼠标期间使用的光标的 CSS 值。例如,activeCursor: "grabbing"
可以改变拖动时的光标样式。
allowContextMenu
allowContextMenu: Boolean
默认值: false
如果为 true
,允许在拖动时显示上下文菜单(比如右键单击或长按触摸)。一般在拖动的过程中,这个是被禁用的(特别是触摸设备)。
allowEventDefault
allowEventDefault: Boolean
默认值: false
如果为 true
,则不会在原始鼠标/指针/触摸事件上调用 preventDefault()
。这在希望允许默认行为(如触摸滚动)时很有用。
allowNativeTouchScrolling
allowNativeTouchScrolling: Boolean
默认值: true
如果设置为 true
,允许在相反的方向上原生触摸滚动。
例如,type: 'x' | type: 'left'
的 Draggable 会允许垂直方向上的原生触摸滚动,而 type: 'y' | type: 'top'
允许在水平方向的原生触摸滚动。
autoScroll
autoScroll: Boolean
默认值: 0
当被拖动物体在可滚动容器的 40px 边界时开始自动滚动。
将其设置为一个非 0 值,1
表示正常速度,2
表示 2 倍速度等。
为了更自然的体验,当鼠标越靠近边缘,滚动速度也越快。
⭐ bounds
bounds: Element | String | Object
定义一个元素或选择器,使可拖动元素保持在另一个 DOM 元素的边界内。比如 bounds: ".container"
。
还可以定义矩形边界:bounds: { top: 100, left: 0, width: 1000, height: 800 }
,它是基于父元素的坐标系统的。
或者定义特定的最大和最小值,比如 bounds: { minX: 10, maxX: 300, minY: 50, maxY: 500 }
或者 bounds: { minRotation: 0, maxRotation: 270 }
。
callbackScope
callbackScope: Object
定义所有回调函数(如 onDrag
, onDragEnd
, onDragStart
等)的执行上下文,即 this
。
clickableTest
clickableTest: Function
定义一个函数,用于判断哪些元素被认为是“可点击”的。当用户按下鼠标或手指时,Draggable 会调用此函数,并将目标元素作为参数传递给该函数。
函数返回 true
或 false
,决定是否允许拖动。
⭐ cursor
cursor: String
默认情况下,除非 type: "rotation"
,否则元素的光标 CSS 属性被设置为move
。
dragClickables
dragClickables: Boolean
默认情况下,Draggable 会在几乎所有元素上工作,但有时希望点击 <a> | <button> | <select> | <button> | <textarea>
等元素时不触发拖动,而是触发浏览器的默认行为。
当 dragClickables: false
时,允许点击这些元素并触发默认行为。
⭐ dragResistance
dragResistance: Number
控制拖动时施加的阻力程度,范围为 0 到 1。
1 表示完全不允许拖动,
0.75 应用更多抵抗性(使对象以 1/4 速度运动),
0.5 表示半速
edgeResistance
edgeResistance: Number
控制超出边界(如果有边界限制)时施加的阻力程度,范围为 0 到 1。
1 表示完全不允许超出边界,
0.75 应用更多抵抗性(使对象以 1/4 速度运动),
0.5 表示半速等。
force3D
force3D: Boolean
默认情况下,使用 3D 变换(如果浏览器支持)以强制元素位于 GPU 的独立层上,从而加速合成。通常提供最佳性能,但可以通过 force3D: false
禁用。
inertia
inertia: Boolean | Object
启用惯性插件,在用户释放鼠标(或触摸结束)后应用基于动量的运动。
设置为 true
时,Draggable 会自动应用 InertiaPlugin 动画。还可以定义更复杂的惯性对象。
注意
InertiaPlugin 是会员插件。
⭐ liveSnap
liveSnap: Function | Array | Object | Boolean
允许你定义当元素正在被拖动时吸附的规则。
函数形式
这个函数将被传递一个数值参数,即自然结束值。函数必须返回新的结束值。
liveSnap: function(endValue) {
return Math.round(endValue / 50) * 50
}
数组形式
遍历数组并找到最接近的数字。
liveSnap: [10, 50, 200, 450];
对象形式
对每个属性使用不同的逻辑。
// 你可以在这个对象中定义点属性,结合 x 和 y
// 它会在20px(距离)范围内的数组中任意点上对齐
liveSnap: {
points: [{x: 0, y: 0}, {x: 100, y: 0}],
radius: 20
}
// 可以使用基于函数的值来运行自己的捕获逻辑
liveSnap: {
points: function(point) {
// 自定义逻辑
}
}
⭐ lockAxis
lockAxis: Boolean
默认情况下,拖动时可以沿任意方向移动。
如果要限制拖动方向,可以设置 lockAxis: true
。
只要在某个方向(水平或者垂直方向)移动超过 2px
,就会锁定为该方向,不允许对角线的移动。
minimumMovement
minimumMovement: Number
默认情况下,Draggable 需要被拖动元素移动超过 2px
才被认为是拖动操作,但是你可以通过 minimumMovement
改变这个阈值。
minimumMovement: 6
表示超过 6px
才认为是拖动操作。
⭐ trigger
trigger: Element | String | Object
如果你只想某个特定区域(比如 window 的 topBar 位置)触发拖动而不是整个元素,你可以定义一个子元素作为 trigger
。
{
trigger: topBarElement;
}
{
trigger: "#top-Bar";
}
{
trigger: "$('#top-bar')";
}
⭐ type
type: String
默认值: "x,y"
拖动的类型。
type: "x,y"; // 即 translateX & translateY
type: "x";
type: "y";
type: "left,top";
type: "left";
type: "top";
type: "rotation";
zIndexBoost
zIndexBoost: Boolean
默认值: true
默认情况下,对于水平或者垂直方向拖动,当一个元素被按压时,它的 zIndex 会设置为一个比较高的值(默认 1000)。
并且数值也会随着新的按压元素增加,这样堆叠顺序看起来是正确的(新按下的对象上升到顶部),但是你可以通过 zIndexBoost: false 跳过这种行为。
回调函数
onClickParams
onClickParams: Array
传递给 onClick
函数的参数数组。
onClick
onClick: Function
在点击元素时调用。
释放前没有移动超过 3px
时被被当做点击处理,这个函数被调用。这用于分辨用户意图:点击还是拖动。
Draggable.create(".draggable", {
onClickParams: ["onClick", Date.now()],
onClick: (message, nowTime) => {
console.log(message, nowTime);
},
});
onDragParams
onDragParams: Array
传递给 onDrag
函数的参数数组。
⭐ onDrag
onDrag: Function
在元素拖动过程中调用。
Draggable.create(".draggable", {
onDragParams: ["onDrag", Date.now()],
onDrag: (message, nowTime) => {
console.log(message, nowTime);
},
});
onDragEndParams
onDragEndParams: Array
传递给 onDragEnd
函数的参数数组。
⭐ onDragEnd
onDragEnd: Function
在元素拖动结束时调用。
Draggable.create(".draggable", {
onDragEndParams: ["onDragEnd", Date.now()],
onDragEnd: (message, nowTime) => {
console.log(message, nowTime);
},
});
onDragStartParams
onDragStartParams: Array
传递给 onDragStart
函数的参数数组。
⭐ onDragStart
onDragStart: Function
在元素拖动开始时调用。
Draggable.create(".draggable", {
onDragStartParams: ["onDragStart", Date.now()],
onDragStart: (message, nowTime) => {
console.log(message, nowTime);
},
});
onLockAxis
onLockAxis: Function
在拖动轴被锁定时调用。
前提是 lockAxis: true
。
Draggable.create(".draggable", {
lockAxis: true,
onLockAxis: () => {
console.log("Axis locked");
},
});
onMove
onMove: Function
每次鼠标在拖拽时移动就会调用。
Draggable.create(".draggable", {
onMove: () => {
console.log("Moving");
},
});
提示
通常,最好使用 onDrag
,但是如果你需要拖拽事件中的 .stopPropogation | .stopImmediatePropogation
时,可以用这个函数。
onPressParams
onPressParams: Array
传递给 onPress
函数的参数数组。
onPress
onPress: Function
鼠标按下时调用。
Draggable.create(".draggable", {
onPressParams: ["onPress", Date.now()],
onPress: (message, nowTime) => {
console.log(message, nowTime);
},
});
onPressInit
onPressInit: Function
在 onPress
记录起始值之前调用。
Draggable.create(".draggable", {
onPressInit: () => {
console.log("Press Init");
},
});
提示
onPressInit
总是在 onPress
之前触发。
onReleaseParams
onReleaseParams: Array
传递给 onRelease
函数的参数数组。
onRelease
onRelease: Function
鼠标释放时调用。
Draggable.create(".draggable", {
onReleaseParams: ["onRelease", Date.now()],
onRelease: (message, nowTime) => {
console.log(message, nowTime);
},
});
Snapping 用法
Draggable 拥有高级的吸附能力。
可以在 config
中定义 snap
值来控制元素拖拽释放后的位置,或者你可以定义 liveSnap
定义拖动时 实时吸附。
数组形式
Draggable.create("#id", {
type: "x,y",
liveSnap: {
// 吸附到数组中最近的点,但只能在 `15px` 范围内
points: [
{ x: 0, y: 0 },
{ x: 100, y: 0 },
{ x: 200, y: 50 },
],
radius: 15,
},
});
points
是一个特殊的属性,允许你在单一地方结合 x
和 y
的逻辑。
Draggable.create("#id", {
type: "x,y",
liveSnap: {
// x & y(或者 top & left)可以都有自己的吸附数组
x: [0, 100, 200, 300],
y: [0, 50, 100, 150],
},
});
函数形式
Draggable.create("#id", {
type: "x,y",
liveSnap: {
// 吸附到数组中最近的点,但只能在 `15px` 范围内
points: function (point) {
// 如果在100px内,吸附到 500,250 位置
const dx = point.x - 500;
const dy = point.y - 250;
if (Matt.sqrt(dx * dx, dy * dy) < 100) {
// 100px半径内
return { x: 500, y: 250 };
}
return point; // 否则不做任何改变
},
},
});
或者单独设置每个属性。
Draggable.create('#id', {
type: 'x,y',
liveSnap: {
x: function(value) {
// x方向吸附增量为 50
return Math.round(value / 50) * 50
},
y: function(value) {
// y方向吸附增量为 25
return Math.round(value / 25) * 2s5
}
}
})
rotation
也是一样的。
Draggable.create("#id", {
type: "rotation",
liveSnap: {
rotation: function (value) {
// 旋转吸附增量为 10
return Math.round(value / 10) * 10;
},
},
});
注意事项
如果你想特殊的元素可点击,并被 Draggable 所忽略,只需给该元素添加
data-clickable="true"
,或者添加onclick
。
默认情况,Draggable 会自动忽略<a> & <input> & <select> & <button> & <textarea>
元素的点击。
如果你想运行自定义逻辑来决定一个对象是否被认定为可点击,可以设置clickableTest
配置属性,通过该函数返回true | false
来判断。为了使物体基于
top & left
CSS 属性移动,你必须确保它们的position
属性为relative | absolute
。如果你混用
timelines
和draggable
,你需要使用代理元素。