{{ percent }}
基于vue制作手机端气泡滑块拖动ui交互,线条上滑块拖动气泡提示ui特效。
Html代码
创意的气泡滑块拖动vue特效
css代码
:root {
--color-primary: #5626e8;
--color-secondary: #d2daef;
--color-bg: #f4f6fe;
}
* {
box-sizing: border-box;padding:0;margin:0;
}
body {
background-color: var(--color-bg);
}
main {
width: 100%;
min-height: 100vh;
display: -webkit-box;
display: flex;
-webkit-box-pack: center;
justify-content: center;
-webkit-box-align: center;
align-items: center;
}
.sliderContainer {
position: relative;
width: 100%;
display: -webkit-box;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
flex-direction: column;
-webkit-box-align: center;
align-items: center;
padding: 3em;
}
.sliderContainer .sliderContainer__input {
width: 100%;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
outline: none;
height: 0.2em;
background-image: -webkit-gradient(
linear,
left top,
right top,
color-stop(50%, var(--color-primary)),
color-stop(50%, var(--color-secondary))
);
border-radius: 1em;
margin: 0;
}
.sliderContainer .sliderContainer__thumb {
cursor: pointer;
width: 1.5em;
height: 1.5em;
border: 0.5em solid var(--color-primary);
border-radius: 100%;
background-color: white;
position: absolute;
top: 50%;
-webkit-transform: translateY(-50%);
transform: translateY(-50%);
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.ballon {
--size: 4em;
width: var(--size);
height: var(--size);
display: -webkit-box;
display: flex;
-webkit-box-pack: center;
justify-content: center;
-webkit-box-align: center;
align-items: center;
background-color: var(--color-primary);
border-bottom-left-radius: 50%;
position: absolute;
border-top-left-radius: 50%;
border-top-right-radius: 50%;
top: -1.2em;
opacity: 0;
}
.ballon .ballon__text {
color: white;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
font-family: Arial, Helvetica, sans-serif;
}
.ballon::after {
content: "";
position: absolute;
width: 0;
height: 0;
border-left: 0.25em solid transparent;
border-right: 0.25em solid transparent;
-webkit-transform: rotate(-45deg);
transform: rotate(-45deg);
border-bottom: 0.25em solid var(--color-primary);
top: 98%;
left: 94%;
}
@media (max-width: 768px) {
.sliderContainer {
min-width: 95%;
}
}
let currentDeltaX = 0;
//Utils
const getElementOffset = el => el.getBoundingClientRect();
const getPercentInBetween = ({ value, min, max }) => {
return (value - min) / (max - min) * 100;
};
const getValueInBetween = (value, { min, max }) => {
if (value < min) return min;
if (value > max) return max;
return value;
};
const getSliderValue = () => {
const getRef = ref => app.$refs[ref];
const { left: thumbElLeft, width: thumbElWidth } = getElementOffset(
getRef("thumbEl"));
const { left: inputElLeft, right: inputElRight } = getElementOffset(
getRef("inputEl"));
const thumbCenterPoint = thumbElLeft + thumbElWidth / 2;
const rangeValue = getPercentInBetween({
value: thumbCenterPoint,
min: inputElLeft,
max: inputElRight });
const percent = Math.round(
getValueInBetween(rangeValue, { min: 0, max: 100 }));
return percent;
};
const getBallonScaleValue = () =>
1 + getValueInBetween(getSliderValue(), { min: 1, max: 99 }) / 480;
//-------------------
const app = new Vue({
el: "main",
data: {
isDragging: false,
percent: 50,
initialThumbElClientRect: null },
mounted: function () {
const getRef = ref => this.$refs[ref];
TweenLite.set(getRef("thumbEl"), {
y: "-50%" });
TweenLite.set(getRef("ballonEl"), {
rotation: 45 });
this.initialThumbElClientRect = getElementOffset(getRef("thumbEl"));
},
methods: {
getRef: function (ref) {
return this.$refs[ref];
},
dragStart: function () {
this.isDragging = true;
TweenLite.set(this.getRef("ballonEl"), {
rotation: 45 });
this.percent = getSliderValue();
this.toggleSliderAnimation();
},
dragEnd: function () {
this.isDragging = false;
this.toggleSliderAnimation();
},
drag: function (event) {
if (this.isDragging) {
const { deltaX: newDeltaX, velocityX } = event;
const {
left: thumbElInitialOffsetLeft,
right: thumbElInitialOffsetRight,
width: thumbElWidth } =
this.initialThumbElClientRect;
const {
left: inputElOffsetLeft,
right: inputElOffsetRight } =
getElementOffset(this.getRef("inputEl"));
const xMovement = newDeltaX + currentDeltaX;
const hasGoneTooLeft =
thumbElInitialOffsetLeft + xMovement <
inputElOffsetLeft - thumbElWidth / 2;
const hasGoneTooRight =
thumbElInitialOffsetRight + xMovement >
inputElOffsetRight + thumbElWidth / 2;
if (hasGoneTooLeft || hasGoneTooRight) return;
this.moveThumb({ x: xMovement, velocityX });
}
},
moveThumb: function ({ x, velocityX }) {
const sliderValue = getSliderValue();
const tl = new TimelineLite();
const scaleValue = getBallonScaleValue();
tl.
to(this.getRef("thumbEl"), 0.001, {
x,
ease: Power4.ease }).
to(this.getRef("ballonEl"), 0.2, {
x,
scale: scaleValue,
rotation:
45 + getValueInBetween(velocityX, { min: -7, max: 7 }) * -15,
ease: Power4.ease });
this.getRef("inputEl").style.backgroundImage = `-webkit-gradient(
linear,
left top,
right top,
color-stop(${sliderValue}%, var(--color-primary)),
color-stop(${sliderValue}%, var(--color-secondary))
)`;
this.percent = sliderValue;
},
toggleSliderAnimation: function () {
const that = this;
const tl = new TimelineLite();
const sliderValue = getSliderValue();
const scaleValue = this.isDragging ? getBallonScaleValue() : 0.8;
const shouldFlyBalloon = sliderValue >= 80 && sliderValue <= 100;
const toggleThumbAnim = () => {
tl.to(that.getRef("thumbEl"), 0.23, {
width: that.isDragging ? "2.6em" : "1.5em",
height: that.isDragging ? "2.6em" : "1.5em",
borderWidth: that.isDragging ? "2px" : "0.5em",
ease: Power4.easeOut });
};
const toggleBallonAnim = () => {
tl.to(
that.getRef("ballonEl"),
0.5,
{
opacity: that.isDragging ? 1 : 0,
scale: scaleValue,
y: that.isDragging ? "-75%" : "5%",
ease: that.isDragging ? Power1.easeOut : Power1.easeIn },
"-0.23");
};
if (this.isDragging) {
toggleThumbAnim();
toggleBallonAnim();
} else {
toggleThumbAnim();
if (shouldFlyBalloon) {
tl.to(this.getRef("ballonEl"), 1.7, {
y: -window.innerHeight / 2,
opacity: 0,
ease: Power1.easeOut,
onComplete: () => {
TweenLite.set(this.getRef("ballonEl"), {
y: "5%" });
} });
} else {
toggleBallonAnim();
}
}
} } });
const getRef = ref => app.$refs[ref];
const appHammer = new Hammer.Manager(getRef("thumbEl"));
appHammer.add(new Hammer.Pan({ threshold: 0 }));
appHammer.on("pan", app.drag);
appHammer.on("panend", ({ deltaX }) => {
currentDeltaX += deltaX;
});
window.addEventListener("mouseup", app.dragEnd, false);
window.addEventListener("touchend", app.dragEnd, false);