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的基礎上封裝插件,有生之年見~

圖片描述

原文地址 在此 ,歡迎來玩~
相關文章
相關標籤/搜索