调光开光(Dimmer Switch)

如何在界面中添加一个调光开关?


更多有趣示例 尽在 知屋安砖社区

示例

在这里插入图片描述

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()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值