最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在iOS中實(shí)現(xiàn)谷歌滅霸彩蛋的完整示例

 更新時(shí)間:2019年05月06日 09:18:28   作者:potato04  
這篇文章主要給大家介紹了關(guān)于如何在iOS中實(shí)現(xiàn)谷歌滅霸彩蛋的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位iOS開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

前言

最近上映的復(fù)仇者聯(lián)盟4據(jù)說(shuō)沒(méi)有片尾彩蛋,不過(guò)谷歌幫我們做了。只要在谷歌搜索滅霸,在結(jié)果的右側(cè)點(diǎn)擊無(wú)限手套,你將化身為滅霸,其中一半的搜索結(jié)果會(huì)化為灰燼消失...那么這么酷的動(dòng)畫(huà)在iOS中可以實(shí)現(xiàn)嗎?答案是肯定的。整個(gè)動(dòng)畫(huà)主要包含以下幾部分:響指動(dòng)畫(huà)、沙化消失以及背景音效和復(fù)原動(dòng)畫(huà),讓我們分別來(lái)看看如何實(shí)現(xiàn)。

圖1 左為沙化動(dòng)畫(huà),右為復(fù)原動(dòng)畫(huà)

響指動(dòng)畫(huà)

Google的方法是利用了48幀合成的一張Sprite圖進(jìn)行動(dòng)畫(huà)的:

圖2 響指Sprite圖片

原始圖片中48幅全部排成一行,這里為了顯示效果截成2行

iOS 中通過(guò)這張圖片來(lái)實(shí)現(xiàn)動(dòng)畫(huà)并不難。CALayer有一個(gè)屬性contentsRect,通過(guò)它可以控制內(nèi)容顯示的區(qū)域,而且是Animateable的。它的類(lèi)型是CGRect,默認(rèn)值為(x:0.0, y:0.0, width:1.0, height:1.0),它的單位不是常見(jiàn)的Point,而是單位坐標(biāo)空間,所以默認(rèn)值顯示100%的內(nèi)容區(qū)域。新建Sprite播放視圖層AnimatableSpriteLayer:

class AnimatableSpriteLayer: CALayer {
 private var animationValues = [CGFloat]()
 convenience init(spriteSheetImage: UIImage, spriteFrameSize: CGSize ) {
 self.init()
 //1
 masksToBounds = true
 contentsGravity = CALayerContentsGravity.left
 contents = spriteSheetImage.cgImage
 bounds.size = spriteFrameSize
 //2
 let frameCount = Int(spriteSheetImage.size.width / spriteFrameSize.width)
 for frameIndex in 0..<frameCount {
  animationValues.append(CGFloat(frameIndex) / CGFloat(frameCount))
 }
 }
 
 func play() {
 let spriteKeyframeAnimation = CAKeyframeAnimation(keyPath: "contentsRect.origin.x")
 spriteKeyframeAnimation.values = animationValues
 spriteKeyframeAnimation.duration = 2.0
 spriteKeyframeAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
 //3
 spriteKeyframeAnimation.calculationMode = CAAnimationCalculationMode.discrete
 add(spriteKeyframeAnimation, forKey: "spriteKeyframeAnimation")
 }
}

//1: masksToBounds = true和contentsGravity = CALayerContentsGravity.left是為了當(dāng)前只顯示Sprite圖的第一幅畫(huà)面

//2: 根據(jù)Sprite圖大小和每幅畫(huà)面的大小計(jì)算出畫(huà)面數(shù)量,預(yù)先計(jì)算出每幅畫(huà)面的contentsRect.origin.x偏移量

//3: 這里是關(guān)鍵,指定關(guān)鍵幀動(dòng)畫(huà)的calculationMode為discrete確保關(guān)鍵幀動(dòng)畫(huà)依次使用values中指定的關(guān)鍵幀值進(jìn)行變化,而不是默認(rèn)情況下采用線性插值進(jìn)行過(guò)渡,來(lái)個(gè)對(duì)比圖可能比較容易理解:

圖3 左邊為離散模式,右邊為默認(rèn)的線性模式

沙化消失

這個(gè)效果是整個(gè)動(dòng)畫(huà)較難的部分,Google的實(shí)現(xiàn)很巧妙,它將需要沙化消失內(nèi)容的html通過(guò)html2canvas渲染成canvas,然后將其轉(zhuǎn)換為圖片后的每一個(gè)像素點(diǎn)隨機(jī)地分配到32塊canvas中,最后對(duì)每塊畫(huà)布進(jìn)行隨機(jī)地移動(dòng)和旋轉(zhuǎn)即達(dá)到了沙化消失的效果。

像素處理

新建自定義視圖 DustEffectView,這個(gè)視圖的作用是用來(lái)接收?qǐng)D片并將其進(jìn)行沙化消失。首先創(chuàng)建函數(shù)createDustImages,它將一張圖片的像素隨機(jī)分配到32張等待動(dòng)畫(huà)的圖片上:

class DustEffectView: UIView {
 private func createDustImages(image: UIImage) -> [UIImage] {
 var result = [UIImage]()
 guard let inputCGImage = image.cgImage else {
  return result
 }
 //1
 let colorSpace = CGColorSpaceCreateDeviceRGB()
 let width = inputCGImage.width
 let height = inputCGImage.height
 let bytesPerPixel = 4
 let bitsPerComponent = 8
 let bytesPerRow = bytesPerPixel * width
 let bitmapInfo = CGImageAlphaInfo.premultipliedLast.rawValue | CGBitmapInfo.byteOrder32Little.rawValue
 
 guard let context = CGContext(data: nil, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
  return result
 }
 context.draw(inputCGImage, in: CGRect(x: 0, y: 0, width: width, height: height))
 guard let buffer = context.data else {
  return result
 }
 let pixelBuffer = buffer.bindMemory(to: UInt32.self, capacity: width * height)
 //2
 let imagesCount = 32
 var framePixels = Array(repeating: Array(repeating: UInt32(0), count: width * height), count: imagesCount)
 for column in 0..<width {
  for row in 0..<height {
  let offset = row * width + column
  //3
  for _ in 0...1 { 
   let factor = Double.random(in: 0..<1) + 2 * (Double(column)/Double(width))
   let index = Int(floor(Double(imagesCount) * ( factor / 3)))
   framePixels[index][offset] = pixelBuffer[offset]
  }
  }
 }
 //4
 for frame in framePixels {
  let data = UnsafeMutablePointer(mutating: frame)
  guard let context = CGContext(data: data, width: width, height: height, bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo) else {
  continue
  }
  result.append(UIImage(cgImage: context.makeImage()!, scale: image.scale, orientation: image.imageOrientation))
 }
 return result
 }
}

//1: 根據(jù)指定格式創(chuàng)建位圖上下文,然后將輸入的圖片繪制上去之后獲取其像素?cái)?shù)據(jù)

//2: 創(chuàng)建像素二維數(shù)組,遍歷輸入圖片每個(gè)像素,將其隨機(jī)分配到數(shù)組32個(gè)元素之一的相同位置。隨機(jī)方法有點(diǎn)特別,原始圖片左邊的像素只會(huì)分配到前幾張圖片,而原始圖片右邊的像素只會(huì)分配到后幾張。

圖4 上部分為原始圖片,下部分為像素分配后的32張圖片依次顯示效果

//3: 這里循環(huán)2次將像素分配兩次,可能 Google 覺(jué)得只分配一遍會(huì)造成像素比較稀疏。個(gè)人認(rèn)為在移動(dòng)端,只要一遍就好了。

//4: 創(chuàng)建32張圖片并返回

添加動(dòng)畫(huà)

Google的實(shí)現(xiàn)是給canvas中css的transform屬性設(shè)置為rotate(deg) translate(px, px) rotate(deg),值都是隨機(jī)生成的。如果你對(duì)CSS的動(dòng)畫(huà)不熟悉,那你會(huì)覺(jué)得在iOS中只要添加三個(gè)CABasicAnimation然后將它們添加到AnimationGroup就好了嘛,實(shí)際上并沒(méi)有那么簡(jiǎn)單... 因?yàn)镃SS的transform中后一個(gè)變換函數(shù)是基于前一個(gè)變換后的新transform坐標(biāo)系。假如某張圖片的動(dòng)畫(huà)樣式是這樣的:rotate(90deg) translate(0px, 100px) rotate(-90deg) 直覺(jué)告訴我應(yīng)該是旋轉(zhuǎn)著向下移動(dòng)100px,然而在CSS中的元素是這么運(yùn)動(dòng)的:

圖5 CSS中transform多值動(dòng)畫(huà)

第一個(gè)rotate和translate決定了最終的位置和運(yùn)動(dòng)軌跡,至于第二個(gè)rotate作用,只是疊加第一個(gè)rotate的值作為最終的旋轉(zhuǎn)弧度,這里剛好為0也就是不旋轉(zhuǎn)。那么在iOS中該如何實(shí)現(xiàn)相似的運(yùn)動(dòng)軌跡呢?可以利用UIBezierPath, CAKeyframeAnimation的屬性path可以指定這個(gè)UIBezierPath為動(dòng)畫(huà)的運(yùn)動(dòng)軌跡。確定起點(diǎn)和實(shí)際終點(diǎn)作為貝塞爾曲線的起始點(diǎn)和終止點(diǎn),那么如何確定控制點(diǎn)?好像可以將“預(yù)想”的終點(diǎn)(下圖中的(0,-1))作為控制點(diǎn)。

圖6 將“預(yù)想”的終點(diǎn)作為控制點(diǎn)的貝塞爾曲線,看起來(lái)和CSS中的運(yùn)動(dòng)軌跡差不多

擴(kuò)展問(wèn)題

通過(guò)文章中描述的方式生成的貝塞爾曲線是否與CSS中的動(dòng)畫(huà)軌跡完全一致呢?

現(xiàn)在可以給視圖添加動(dòng)畫(huà)了:

 let layer = CALayer()
 layer.frame = bounds
 layer.contents = image.cgImage
 self.layer.addSublayer(layer)
 let centerX = Double(layer.position.x)
 let centerY = Double(layer.position.y)
 let radian1 = Double.pi / 12 * Double.random(in: -0.5..<0.5)
 let radian2 = Double.pi / 12 * Double.random(in: -0.5..<0.5)
 let random = Double.pi * 2 * Double.random(in: -0.5..<0.5)
 let transX = 60 * cos(random)
 let transY = 30 * sin(random)
 //1: 
 // x' = x*cos(rad) - y*sin(rad)
 // y' = y*cos(rad) + x*sin(rad)
 let realTransX = transX * cos(radian1) - transY * sin(radian1)
 let realTransY = transY * cos(radian1) + transX * sin(radian1)
 let realEndPoint = CGPoint(x: centerX + realTransX, y: centerY + realTransY)
 let controlPoint = CGPoint(x: centerX + transX, y: centerY + transY)
 //2:
 let movePath = UIBezierPath()
 movePath.move(to: layer.position)
 movePath.addQuadCurve(to: realEndPoint, controlPoint: controlPoint)
 let moveAnimation = CAKeyframeAnimation(keyPath: "position")
 moveAnimation.path = movePath.cgPath
 moveAnimation.calculationMode = .paced
 //3:   
 let rotateAnimation = CABasicAnimation(keyPath: "transform.rotation")
 rotateAnimation.toValue = radian1 + radian2
 let fadeOutAnimation = CABasicAnimation(keyPath: "opacity")
 fadeOutAnimation.toValue = 0.0
 let animationGroup = CAAnimationGroup()
 animationGroup.animations = [moveAnimation, rotateAnimation, fadeOutAnimation]
 animationGroup.duration = 1
 //4:
 animationGroup.beginTime = CACurrentMediaTime() + 1.35 * Double(i) / Double(imagesCount)
 animationGroup.isRemovedOnCompletion = false
 animationGroup.fillMode = .forwards
 layer.add(animationGroup, forKey: nil)

//1: 實(shí)際的偏移量旋轉(zhuǎn)了radian1弧度,這個(gè)可以通過(guò)公式x' = x*cos(rad) - y*sin(rad), y' = y*cos(rad) + x*sin(rad)算出

//2: 創(chuàng)建UIBezierPath并關(guān)聯(lián)到CAKeyframeAnimation中

//3: 兩個(gè)弧度疊加作為最終的旋轉(zhuǎn)弧度

//4: 設(shè)置CAAnimationGroup的開(kāi)始時(shí)間,讓每層Layer的動(dòng)畫(huà)延遲開(kāi)始

結(jié)尾

到這里,谷歌滅霸彩蛋中較復(fù)雜的技術(shù)點(diǎn)均已實(shí)現(xiàn)。如果您感興趣,完整的代碼(包含音效和復(fù)原動(dòng)畫(huà))可以通過(guò)文章開(kāi)頭的鏈接進(jìn)行查看,可以嘗試將沙化圖片的數(shù)量從32提高至更多,效果會(huì)越好,內(nèi)存也會(huì)消耗更多 :-D。

示例代碼下載

參考資料

總結(jié)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • IOS 照片編輯的view封裝的實(shí)例詳解

    IOS 照片編輯的view封裝的實(shí)例詳解

    這篇文章主要介紹了IOS 照片編輯的view封裝的實(shí)例詳解的相關(guān)資料,這里提供實(shí)例幫助大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下
    2017-08-08
  • iOS中id類(lèi)型的理解及底層原理詳解

    iOS中id類(lèi)型的理解及底層原理詳解

    這篇文章主要給大家介紹了關(guān)于iOS中id類(lèi)型的理解以及底層原理的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-07-07
  • IOS 添加自定義字體方法詳解

    IOS 添加自定義字體方法詳解

    這篇文章主要介紹了IOS 添加自定義字體方法詳解的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • IOS開(kāi)發(fā)中禁止NavigationController的向右滑動(dòng)返回

    IOS開(kāi)發(fā)中禁止NavigationController的向右滑動(dòng)返回

    這篇文章主要介紹了IOS開(kāi)發(fā)中禁止NavigationController的向右滑動(dòng)返回的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • Reactnative-iOS回調(diào)Javascript的方法

    Reactnative-iOS回調(diào)Javascript的方法

    這篇文章主要介紹了Reactnative-iOS回調(diào)Javascript的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-09-09
  • 淺談SwiftUI 里面$0是什么意思如何用

    淺談SwiftUI 里面$0是什么意思如何用

    這篇文章主要介紹了淺談SwiftUI 里面$0是什么意思如何用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • IOS setOnclick點(diǎn)擊事件分析

    IOS setOnclick點(diǎn)擊事件分析

    本篇文章給大家整理了IOS setOnclick點(diǎn)擊事件完美擴(kuò)展的相關(guān)知識(shí)點(diǎn)以及代碼實(shí)例,有需要的朋友可以跟著測(cè)試學(xué)習(xí)下。
    2018-05-05
  • ios基于UICollectionView實(shí)現(xiàn)橫向瀑布流

    ios基于UICollectionView實(shí)現(xiàn)橫向瀑布流

    這篇文章主要為大家詳細(xì)介紹了ios基于UICollectionView實(shí)現(xiàn)橫向瀑布流,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-12-12
  • 支付寶支付開(kāi)發(fā)IOS圖文教程案例

    支付寶支付開(kāi)發(fā)IOS圖文教程案例

    這篇文章主要介紹了支付寶支付開(kāi)發(fā)IOS案例的相關(guān)資料,需要的朋友可以參考下
    2016-04-04
  • IOS實(shí)現(xiàn)視頻動(dòng)畫(huà)效果的啟動(dòng)圖

    IOS實(shí)現(xiàn)視頻動(dòng)畫(huà)效果的啟動(dòng)圖

    這篇文章實(shí)現(xiàn)的是一個(gè)關(guān)于啟動(dòng)頁(yè)或者引導(dǎo)頁(yè)的視頻動(dòng)畫(huà)效果的實(shí)現(xiàn)過(guò)程,對(duì)于大家開(kāi)發(fā)APP具有一定的參考借鑒價(jià)值,有需要的可以來(lái)看看。
    2016-09-09

最新評(píng)論

普洱| 青岛市| 乐清市| 仁化县| 恩平市| 八宿县| 阿克| 高淳县| 克东县| 黑河市| 绍兴县| 衢州市| 曲周县| 黄浦区| 平山县| 定结县| 自治县| 白水县| 南江县| 黄石市| 安化县| 司法| 九龙城区| 东源县| 峨山| 宁强县| 安陆市| 鄂尔多斯市| 桐城市| 布尔津县| 台南市| 和林格尔县| 淄博市| 江津市| 定日县| 河东区| 平阳县| 中牟县| 科技| 阳山县| 乐昌市|