基于vue制作手机端气泡滑块拖动ui交互,线条上滑块拖动气泡提示ui特效。
Html代码:
<!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>创意的气泡滑块拖动vue特效</title> <link rel="stylesheet" href="css/style.css"> </head> <body> <main> <div class="sliderContainer"> <div class="ballon" ref="ballonEl"> <span class="ballon__text" ref="ballonTextEl">{{ percent }}</span> </div> <div v-on:mousedown="dragStart" v-on:touchstart="dragStart" ref="thumbEl" class="sliderContainer__thumb"></div> <div class="sliderContainer__input" ref="inputEl"></div> </div> </main> <script src='js/vue.min.js'></script> <script src='js/hammer.min.js'></script> <script src='js/TweenMax.min.js'></script> <script src="js/script.js"></script> </body> </html>
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 {
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;
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 {
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;
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;
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;
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- 768px) {
.sliderContainer {
min- 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, 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, 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, { 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);