JS仿《阿丽塔》中依德医生的旋转缩放控件 — DEMO篇

前言

前些天看了《阿丽塔》
感叹酷炫特效的同时,不得不说这个片子灰常之热血!
从新点燃了我粪斗的基情!!
有那么几个瞬间仿佛本身回到了……html

停一下

OK,下面进入正题
在依德医生刚捡回阿丽塔的那一段,有木有发现医生家的设备都颇有意思~
好比那我的皮缝纫机,其灵活程度堪比织网ing的蜘蛛
说到蜘蛛,就想起了西游记里的蜘蛛精。今年下半年……git

图片描述

…………
…………
除了人皮缝纫机,当时还注意到他们屏幕的一个交互颇有趣——github

图片描述

没错,今天的主角出场了
就是用JS作一个相似的旋转缩放控件
先来看一哈最终效果,铛铛:web

图片描述
貌似手感不错?能够点此抢先体验(此版本仅限PC端玩家)app

正文

本篇首先开发一个demo试试水,暂时用鼠标代替手指dom

实现思路以下:函数

  1. 画一个彩色圆环
  2. 监听鼠标移动事件(保证只能做用在圆环上)
  3. 实时计算鼠标当前的角度(相对于圆心)
  4. 对比当前角度和上一次角度,肯定每一帧的旋转方向和距离,根据变化的角度值旋转圆环
  5. 缩放操做外部dom

1. 画圆环

这里使用大圆套小圆组成圆环:大圆设置一个渐变背景色,小圆为纯白色
为啥子不仅用一个圆,而后设置border属性捏?
由于我不喜欢
由于border不能设置渐变色
固然还有另外一个做用:阻止事件(详见下文)this

万事俱备,开始装逼
目标是把圆环用绝对定位放到左上角,并且内圆要适中大小
因此分别设置圆心和大小圆的半径为:spa

var CENTER = { x: 150, y: 150 }, BIG_RADIUS = 150, SMALL_RADIUS = 70

建立div表示两个圆插件

var bigCircle = document.createElement('div')
var smallCircle = document.createElement('div')
document.body.appendChild(bigCircle)
document.body.appendChild(smallCircle)

封装一个函数方便给大小圆添加样式
参数:center圆心、radius半径、bg背景、isMove是否运动(使用CSS3的变化和旋转,每0.16秒运动一次,即60帧)

function setCircleClass(center, radius, bg, isMove) {
    this.style.position = 'absolute'
    this.style.left = center.x - radius + 'px'
    this.style.top = center.y - radius + 'px'
    this.style.width = radius * 2 + 'px'
    this.style.height = radius * 2 + 'px'
    this.style.borderRadius = '50%'
    this.style.zIndex = 66666
    this.style.background = bg
    isMove && (this.style.transition = 'transform linear .016s')
}

调用函数添加样式

setCircleClass.apply(bigCircle, [CENTER, BIG_RADIUS, 'linear-gradient(skyblue, darkorange)', true])
setCircleClass.apply(smallCircle, [CENTER, SMALL_RADIUS, '#FFF', false])

颜色有点瓜皮:
图片描述

2. 监听鼠标事件

监听大圆的mousemove事件,同时小圆阻止事件传播

bigCircle.addEventListener('mousemove', main)
smallCircle.addEventListener('mousemove', function(e) {
    e.stopPropagation()
})

建立大圆的监听函数main
这里设置鼠标左键按下时生效,顺便写一个打印语句

function main(e) {
    if(e.buttons === 1) {
        console.log('鼠标在圆环移动ing')
    }
}

图片描述

3. 实时计算当前角度

接下来就是重头戏了,想要让圆环跟随鼠标转动,首先想到的绝壁是斜率
在监听事件触发时,不停计算鼠标位置和圆心两点连线的斜率,经过对比本次的斜率和上一次斜率,便可得出圆环转动的方向
方向有了,还要计算圆环移动的速度,然而斜率的变化并非线性的,所以很难经过斜率的变化值来计算速度……
因此光有斜率是没办法解决问题的,那么有木有其余线性变化的东西呢……
没错,就是角度了~!

记得Math对象有一些三角函数方法,速速去查

正在眼花缭乱之时,忽然眼角一闪,一个黑衣人从天而降,定睛一看,正是传说中的atan2函数
“骚年,你要找的人正是在下”

打量了一番,发现这哥们不只长得帅,并且手中还拿了一个神器:计算角度函数

function calcAngleDegrees(x, y) {
    return Math.atan2(y, x) * 180 / Math.PI
}

卧槽,简直是踏破铁鞋,赶忙来战:

function main(e) {
    if(e.buttons === 1) {
        var angle = calcAngleDegrees((e.clientX - CENTER.x), (CENTER.y - e.clientY))
        console.log('角度:' + angle)
    }
}

图片描述
如图能够看到,角度的变化是从9点钟方向的180度,顺时针递减360度,回到9点钟方向
正符合咱们后续的需求

图片描述

“大师果真牛皮,不知您的一身好武功是如何修来的?”
atan2笑而不语,一转身便消失在了无尽的夜色中,只留下了无尽的疑问……

既然如此,做为一个热爱技术的搬砖工,我决定本身找出真相~

图片描述
本小节终。

4. 根据角度计算方向和距离,旋转圆环

有了角度值,下面就的问题就引刃而解了

先建立一个函数用来旋转圆环,参数:deg当前圆环的角度

function rotate(deg) {
    this.style.webkitTransform = 'rotate(' + deg + 'deg)'
    this.style.mozTransform = 'rotate(' + deg + 'deg)'
    this.style.msTransform = 'rotate(' + deg + 'deg)'
    this.style.oTransform = 'rotate(' + deg + 'deg)'
    this.style.transform = 'rotate(' + deg + 'deg)'
}

建立变量:当前圆环(大圆)角度值circleAngle、当前鼠标角度值mouseAngle、上一次鼠标角度值lastMouseAngle

var circleAngle = 0, mouseAngle, lastMouseAngle

此处需初始化lastMouseAngle,这个操做看似简单,实则使用正确的姿式能够避免一系列bug
这里研究出来比较好的方法就是在鼠标移入圆环在圆环中按下鼠标的时候赋值,感性趣的童鞋能够自行研究一下

bigCircle.addEventListener('mouseenter', init)
bigCircle.addEventListener('mousedown', init)

function init(e) {
    lastMouseAngle = calcAngleDegrees((e.clientX - CENTER.x), (CENTER.y - e.clientY))
}

初始化lastMouseAngle以后,mouseAngle - lastMouseAngle即为角度的增量

增量正负决定方向:正数为逆时针,负数为顺时针
增量大小决定距离:绝对值便是圆环旋转的角度

因为顺时针旋转时增量为负,且CSS里transform属性为顺时针旋转增长角度
因此当前圆环的角度计算公式为:circleAngle -= (mouseAngle - lastMouseAngle)

改造main函数:

function main(e) {
    if(e.buttons === 1) {
        mouseAngle = calcAngleDegrees((e.clientX - CENTER.x), (CENTER.y - e.clientY))
        var changeMouseAngle = mouseAngle - lastMouseAngle
        circleAngle -= changeMouseAngle
        console.log('当前角度:' + circleAngle)
        rotate.call(this, circleAngle)
        lastMouseAngle = mouseAngle
    }
}

图片描述
能够看到已经有了雏形,愉快地进入下一步

5. 操做外部dom

想要操做外部dom,须要的是一个线性变化的值
用脚趾头都能想到,当前最合适的无疑就是当前圆环角度circleAngle
然鹅仔细观察上一张图就会发现,当鼠标每次移动过9点钟方向时,圆环角度就会瞬间改变360度,回到初始值,并不能知足当前需求图片描述
此处作一波改造,判断当角度的变化值changeMouseAngle超过必定度数的时候,不执行后面的操做
考虑到单身20年用户的手速,暂时设置这个值为300

var MAX_CHANGE_ANGLE = 300
function main(e) {
    ...
    var changeMouseAngle = mouseAngle - lastMouseAngle
    if(Math.abs(changeMouseAngle) > MAX_CHANGE_ANGLE){
        return lastMouseAngle = mouseAngle
    }
    ...
}

这样circleAngle就会呈线性变化了

接下来的事就是找一张阿丽塔的美图了

<div class="pic">
    <img src="./alita.jpg">
</div>
.pic {
    width: 100px;
    margin: 100px auto;
    border-radius: 10px;
}
.pic img {
    width: 100%;
    border-radius: 10px;
}

最后写一段缩放代码:

var picDom = document.getElementsByClassName('pic')[0]

function controlPic(value) {
    this.style.width = 100 + 1 * value + 'px'
}

function main(e) {
    ...
    controlPic.call(picDom, circleAngle)
    ...
}

大功告成!查看完整的代码示例请戳这里,在线体验请戳这里图片描述

后记

从《阿丽塔》上映那天起就开始酝酿这篇博客了,直到一个多月后的今天……

很少BB,下一篇博客将会在demo的基础上封装插件,有生之年见~

图片描述

原文地址 在此 ,欢迎来玩~
相关文章
相关标签/搜索