更多有趣示例 尽在 知屋安砖社区
示例
HTML
.switch
.switch__faceplate
button.switch__button
.switch__button-face
.switch__button-dim-marker
CSS
*
-webkit-tap-highlight-color transparent
box-sizing border-box
:root
--hue floor(math(0, 'random') * 360)
--light 37.5
body
background 'hsl(%s, 50%, %s)' % (var(--hue) calc(var(--light) * 1%))
cursor var(--cursor)
min-height 100vh
touch-action none
.switch
--size 200
--panel-hue 55
--panel-saturation 10
--faceplate 'hsl(55, 10%, %s)' % calc((30 + var(--light)) * 1%)
--faceplate-light 'hsl(55, 10%, %s)' % calc((50 + var(--light)) * 1%)
--faceplate-dark 'hsl(55, 10%, %s)' % calc((0 + var(--light)) * 1%)
--pressed 0
background linear-gradient(0deg, var(--faceplate-dark), var(--faceplate-light))
border-radius 5%
height calc(var(--size) * 1px)
overflow hidden
position absolute
top 50%
left 50%
transform translate(-50%, -50%)
width calc(var(--size) * 1px)
box-shadow '0px 10px 0px 0px hsl(%s, 50%, %s)' % (var(--hue) calc((var(--light) - 10) * 1%))
&__faceplate
background var(--faceplate)
height calc(100% - 10px)
width calc(100% - 10px)
position absolute
top 50%
left 50%
transform translate(-50%, -50%)
border-radius 5%
&:after
&:before
--screw 'hsl(0, 0%, %s)' % calc((20 + var(--light)) * 1%)
--screw-dark 'hsl(0, 0%, %s)' % calc((0 + var(--light)) * 1%)
--screw-darkest 'hsl(0, 0%, %s)' % calc((-20 + var(--light)) * 1%)
content ''
height 8%
width 8%
background linear-gradient(calc(var(--rotation, 30) * 1deg), var(--screw) 0%, var(--screw) 45%, var(--screw-dark) 45%, var(--screw-dark) 55%, var(--screw) 55%)
box-shadow 0px 1px 0px 1px var(--screw-darkest) inset
border-radius 50%
top 50%
transform translate(0, -50%)
position absolute
&:after
--rotation 120
right 10%
&:before
left 10%
&__button
height 35%
width 35%
border 0
background var(--faceplate)
border-radius 50%
position absolute
top 50%
left 50%
transform translate(-50%, -50%)
outline transparent
padding 0
cursor pointer
&:before
content ''
height 99%
width 99%
background var(--faceplate-dark)
position absolute
border-radius 50%
top 50%
left 50%
transform translate(-50%, -50%) translate(0, calc((10 - (var(--pressed) * 10)) * 1px))
transition transform .15s ease 0s
z-index -1
&-face
border 4px solid var(--faceplate-light)
height 100%
width 100%
background var(--faceplate)
border-radius 100%
transform rotate(calc(var(--rotation, 0) * 1deg))
&-dim-marker
position absolute
left 50%
height 44px
width 44px
border-radius 50%
top 10%
transform translate(-50%, -90%)
cursor var(--cursor, 'grab')
&:after
content ''
background 'hsl(0, 100%, %s)' % calc((0 + var(--light)) * 1%)
height 10px
width 10px
border-radius 100%
position absolute
top 100%
left 50%
transform translate(-50%, -50%)
JS
/**
* Poached from HSL Slider: https://codepen.io/jh3y/details/dybjLpa
*/
/**
* Grab the angle between two points
* @param {Object} event - pointer event for current coords
* @param {Object} element - element to use as center point
* @param {Number} buffer - buffer so that angle doesn't allow handle + track overlap
*/
const getAngle = (event, element) => {
const { clientX: x, clientY: y } =
event.touches && event.touches.length ? event.touches[0] : event
const {
x: handleX,
y: handleY,
width: handleWidth,
height: handleHeight,
} = element.getBoundingClientRect()
const handleCenterPoint = {
x: handleX + handleWidth / 2,
y: handleY + handleHeight / 2,
}
const angle =
(Math.atan2(handleCenterPoint.y - y, handleCenterPoint.x - x) * 180) /
Math.PI
return angle
}
const button = document.querySelector('.switch__button')
const dimmer = document.querySelector('.switch__button-dim-marker')
let DOWN_EVENT = 'mousedown'
let MOVE_EVENT = 'mousemove'
let UP_EVENT = 'mouseup'
if (window.PointerEvent) {
// PointerEvent, Chrome
DOWN_EVENT = 'pointerdown'
MOVE_EVENT = 'pointermove'
UP_EVENT = 'pointerup'
} else if ('ontouchstart' in window) {
// Touch Events, iOS Safari
DOWN_EVENT = 'touchstart'
MOVE_EVENT = 'touchmove'
UP_EVENT = 'touchend'
}
let ON = true
const BUFFER = 45
const VARIANTS = {
ON: 60,
OFF: 10,
LOWER: 15,
UPPER: 60,
DIMMED: 0.5,
}
const setLight = () => {
document.documentElement.style.setProperty(
'--light',
ON
? VARIANTS.LOWER + (VARIANTS.UPPER - VARIANTS.LOWER) * VARIANTS.DIMMED
: VARIANTS.OFF
)
}
const dim = e => {
let rotation
const angle = getAngle(e, button)
if (angle > 0) {
rotation = angle - 90
} else {
rotation = 270 + angle
if (angle > -(90 + BUFFER) && angle < -(90 - BUFFER) && angle < -90) {
rotation = 270 - (90 + BUFFER)
VARIANTS.DIMMED = 1
}
if (angle > -(90 + BUFFER) && angle < -(90 - BUFFER) && angle > -90) {
rotation = 270 - (90 - BUFFER)
VARIANTS.DIMMED = 0
}
}
// console.info(rotation)
// Lets work out the dimming here
// If angle is rotation is greater than 0 and less than 180 - BUFFER
// Dim is 0.5 + (angle / 135deg) * 0.5
if (rotation > 0 && rotation < 180 - BUFFER) {
VARIANTS.DIMMED = 0.5 + (rotation / (180 - BUFFER)) * 0.5
} else if (rotation < 0) {
// There's 180 - BUFFER degrees to play with but we know we have 90 degrees here.
VARIANTS.DIMMED = 0.5 - Math.abs(rotation / (180 - BUFFER)) * 0.5
} else if (rotation > 180 + BUFFER && rotation < 270) {
VARIANTS.DIMMED =
((rotation - (180 + BUFFER)) / (180 - (90 + BUFFER))) *
((BUFFER / (180 - BUFFER)) * 0.5)
}
button.style.setProperty('--rotation', rotation)
setLight()
}
const endDim = () => {
// dim(e)
document.documentElement.style.setProperty('--cursor', 'initial')
dimmer.style.setProperty('--cursor', 'grab')
document.body.removeEventListener(MOVE_EVENT, dim)
document.body.removeEventListener(UP_EVENT, endDim)
}
const execute = () => {
ON = !ON
button.style.setProperty('--pressed', 0)
setLight()
document.body.removeEventListener(UP_EVENT, execute)
}
const toggleLight = () => {
button.style.setProperty('--pressed', 1)
document.body.addEventListener(UP_EVENT, execute)
}
const initDim = e => {
e.stopPropagation()
document.documentElement.style.setProperty('--cursor', 'grabbing')
dimmer.style.setProperty('--cursor', 'grabbing')
document.body.addEventListener(MOVE_EVENT, dim)
document.body.addEventListener(UP_EVENT, endDim)
}
dimmer.addEventListener(DOWN_EVENT, initDim)
button.addEventListener(DOWN_EVENT, toggleLight)
setLight()