ios开发 方形到圆的动画_画个圆动画,的两种实现。iOS 动画由很浅,入浅,当然是 Swift...
方法一,使用 CAShapeLayer 和 UIBezierPath
加上 CABasicAnimation 有一个动画属性 strokeEnd
就算完
方法二,复杂一些。频繁调用 CALayer 的 func draw(in ctx: CGContext) 也是可以的
通过定制 CALayer, 还要有一个使用该定制 CALayer 的 custom 视图。使用 @NSManaged, 方便自定制的 CALayer 键值观察 KVC
重写 CALayer 的方法 action(forKey:), 指定需要的动画
重写 CALayer 的方法 needsDisplay(forKey:), 先指定刷新渲染,再出 action(forKey:) 的动画
方法一的,具体实现
class CircleView: UIView {
let circleLayer: CAShapeLayer = {
// 形状图层,初始化与属性配置
let circle = CAShapeLayer()
circle.fillColor = UIColor.clear.cgColor
circle.strokeColor = UIColor.red.cgColor
circle.lineWidth = 5.0
circle.strokeEnd = 0.0
return circle
}()
// 视图创建,通过指定 frame
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
// 视图创建,通过指定 storyboard
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
func setup(){
backgroundColor = UIColor.clear
// 添加上,要动画的图层
layer.addSublayer(circleLayer)
}
override func layoutSubviews() {
super.layoutSubviews()
// 考虑到视图的布局,如通过 auto layout,
// 需动画图层的布局,放在这里
let circlePath = UIBezierPath(arcCenter: CGPoint(x: frame.size.width / 2.0, y: frame.size.height / 2.0), radius: (frame.size.width - 10)/2, startAngle: 0.0, endAngle: CGFloat(Double.pi * 2.0), clockwise: true)
circleLayer.path = circlePath.cgPath
}
// 动画的方法
func animateCircle(duration t: TimeInterval) {
// 画圆形,就是靠 `strokeEnd`
let animation = CABasicAnimation(keyPath: "strokeEnd")
// 指定动画时长
animation.duration = t
// 动画是,从没圆,到满圆
animation.fromValue = 0
animation.toValue = 1
// 指定动画的时间函数,保持匀速
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
// 视图具体的位置,与动画结束的效果一致
circleLayer.strokeEnd = 1.0
// 开始动画
circleLayer.add(animation, forKey: "animateCircle")
}
}
使用的代码 : 很简单
class ViewController: UIViewController {
// storyboard 布局
@IBOutlet weak var circleV: CircleView!
@IBAction func animateFrame(_ sender: UIButton) {
let diceRoll = CGFloat(Int(arc4random_uniform(7))*30)
let circleEdge = CGFloat(200)
// 直接指定 frame 布局
let circleView = CircleView(frame: CGRect(x: 50, y: diceRoll, width: circleEdge, height: circleEdge))
view.addSubview(circleView)
// 开始动画
circleView.animateCircle(duration: 1.0)
}
@IBAction func animateAutolayout(_ sender: UIButton) {
// auto layout 布局
let circleView = CircleView(frame: CGRect.zero)
circleView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(circleView)
circleView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
circleView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
circleView.widthAnchor.constraint(equalToConstant: 250).isActive = true
circleView.heightAnchor.constraint(equalToConstant: 250).isActive = true
// 开始动画
circleView.animateCircle(duration: 1.0)
}
@IBAction func animateStoryboard(_ sender: UIButton) {
// 开始动画
circleV.animateCircle(duration: 1.0)
}
}
方法二的实现
核心类 UICircularRingLayer 的技术注意:
先要自定制一个基于 CAShapeLayer 的图层
对 @NSManaged var val: CGFloat KVC,
触发 override class func needsDisplay(forKey key: String) -> Bool ,
调用 setNeedsDisplay(),重新渲染,
接着触发 override func action(forKey event: String) -> CAAction?, 指定动画,
频繁调用绘制方法 override func draw(in ctx: CGContext), 就是可见的动画
@NSManaged 关键字,类似 Objective-C 里面的 @dynamic 关键字
@NSManaged 关键字,方便键值编码
@NSManaged 通知编译器,不要初始化,运行时保证有值
override class func needsDisplay(forKey key: String) -> Bool 返回 true
就是需要重新渲染,调用 setNeedsDisplay() 方法
下面的
override class func needsDisplay(forKey key: String) -> Bool {
if key == "val" {
return true
} else {
return super.needsDisplay(forKey: key)
}
}
相当于
override class func needsDisplay(forKey key: String) -> Bool {
if key == "val" {
return true
} else {
return false
}
}
override func action(forKey event: String) -> CAAction?, 返回协议对象 CAAction
CAAnimation 遵守 CAAction 协议,这里一般返回个 CAAnimation
一个 CALayer 图层,可以有动态的动画行为。
发起动画时,可以设置该图层的动画属性,操作关联出来的具体动画
下面的
override func action(forKey event: String) -> CAAction? {
if event == "val"{
// 实际动画部分
let animation = CABasicAnimation(keyPath: "val")
// ...
return animation
} else {
return super.action(forKey: event)
}
}
相当于
override func action(forKey event: String) -> CAAction? {
if event == "val"{
// 实际动画部分
let animation = CABasicAnimation(keyPath: "val")
// ...
return animation
} else {
return nil
}
}
方法二的,具体实现
/**
动画起作用的枢纽,
负责处理绘制和动画,
对于使用者隐藏,使用者操作外部的视图类就好
*/
class UICircularRingLayer: CAShapeLayer {
// MARK: 属性
@NSManaged var val: CGFloat
let ringWidth: CGFloat = 20
let startAngle = CGFloat(-90).rads
// MARK: 初始化
override init() {
super.init()
}
override init(layer: Any) {
// 确保使用姿势
guard let layer = layer as? UICircularRingLayer else { fatalError("unable to copy layer") }
super.init(layer: layer)
}
required init?(coder aDecoder: NSCoder) { return nil }
// MARK: 视图渲染部分
/**
重写 draw(in 方法,画圆环
*/
override func draw(in ctx: CGContext) {
super.draw(in: ctx)
UIGraphicsPushContext(ctx)
// 画圆环
drawRing(in: ctx)
UIGraphicsPopContext()
}
// MARK: 动画部分
/**
监听 val 属性的变化,重新渲染
*/
override class func needsDisplay(forKey key: String) -> Bool {
if key == "val" {
return true
} else {
return super.needsDisplay(forKey: key)
}
}
/**
监听 val 属性的变化,指定动画行为
*/
override func action(forKey event: String) -> CAAction? {
if event == "val"{
// 实际动画部分
let animation = CABasicAnimation(keyPath: "val")
animation.fromValue = presentation()?.value(forKey: "val")
animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
animation.duration = 2
return animation
} else {
return super.action(forKey: event)
}
}
/**
画圆,通过路径布局。主要是指定 UIBezierPath 曲线的角度
*/
private func drawRing(in ctx: CGContext) {
let center: CGPoint = CGPoint(x: bounds.midX, y: bounds.midY)
let radiusIn: CGFloat = (min(bounds.width, bounds.height) - ringWidth)/2
// 开始画
let innerPath: UIBezierPath = UIBezierPath(arcCenter: center,
radius: radiusIn,
startAngle: startAngle,
endAngle: toEndAngle,
clockwise: true)
// 具体路径
ctx.setLineWidth(ringWidth)
ctx.setLineJoin(.round)
ctx.setLineCap(CGLineCap.round)
ctx.setStrokeColor(UIColor.red.cgColor)
ctx.addPath(innerPath.cgPath)
ctx.drawPath(using: .stroke)
}
// 本例子中,起始角度固定,终点角度通过 val 设置
var toEndAngle: CGFloat {
return (val * 360.0).rads + startAngle
}
}
辅助方法,用于角度转弧度
extension CGFloat {
var rads: CGFloat { return self * CGFloat.pi / 180 }
}
触发类
自定制 UIView,指定其图层为,之前的定制图层
@IBDesignable open class UICircularRing: UIView {
/**
将 UIView 自带的 layer,强转为上面的 UICircularRingLayer, 方便使用
*/
var ringLayer: UICircularRingLayer {
return layer as! UICircularRingLayer
}
/**
将 UIView 自带的 layer,重写为 UICircularRingLayer
*/
override open class var layerClass: AnyClass {
return UICircularRingLayer.self
}
/**
通过 frame 初始化,的设置
*/
override public init(frame: CGRect) {
super.init(frame: frame)
setup()
}
/**
通过 storyboard 初始化,的设置
*/
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
/**
初始化的配置
*/
func setup(){
// 设置光栅化
// 将光栅化后的内容缓存起来,方便复用
ringLayer.contentsScale = UIScreen.main.scale
ringLayer.shouldRasterize = true
ringLayer.rasterizationScale = UIScreen.main.scale * 2
ringLayer.masksToBounds = false
backgroundColor = UIColor.clear
ringLayer.backgroundColor = UIColor.clear.cgColor
ringLayer.val = 0
}
func startAnimation() {
ringLayer.val = 1
}
}
使用的代码,很简单
class ViewController: UIViewController {
let progressRing = UICircularRing(frame: CGRect(x: 100, y: 100, width: 250, height: 250))
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(progressRing)
}
@IBAction func animate(_ sender: UIButton) {
progressRing.startAnimation()
}
}
方法二,设置线条帽,为圆头,比较方便
ctx.setLineCap(CGLineCap.round)
iOS 设置角度的坐标图
ios开发 方形到圆的动画_画个圆动画,的两种实现。iOS 动画由很浅,入浅,当然是 Swift...相关推荐
- h5画三角形_如何利用css或html5画出一个三角形?两种不同的制作三角形方法(代码实例)...
我们在平时的前端开发的时候,有时候是需要一些小图形来丰富一下页面效果,比如:下拉列表的倒三角图形.那么这样的一个三角形是如何制作出来的,本章给大家介绍如何利用css或html画出一个三角形?两种不同的 ...
- ios开发 方形到圆的动画_使用UIBezierPath画个圆动画
UIBezierPath主要用来绘制矢量图形,它是基于Core Graphics对CGPathRef数据类型和path绘图属性的一个封装,所以是需要图形上下文的(CGContextRef),所以一般U ...
- ios开发 方形到圆的动画_3Blue1Brown 动画制作教程(1)--制作第一个自己的动画
制作第一个自己的动画 前一篇详细介绍了 3Blue1Brown 的动画引擎在 Windows 10 64 位系统上,基于 Anaconda的配置方法,并且详细描述了在配置 3Blue1Brown 提供 ...
- ae制h5文字动画_大杀器Bodymovin和Lottie:把AE动画转换成HTML5/Android/iOS原生动画
前段时间听部门老大说,Airbnb出了个移动端的动画库Lottie,可以和一个名叫Bodymovin的AE插件结合起来,把在AE上做好的动画导出为json文件,然后以Android/iOS原生动画的形 ...
- arcgis飞行轨迹动画_高德地图,百度地图,arcgis地图利用canvas动画绘制圆形扩散、运动轨迹等动态效果...
本文转发自热爱前端知识的博客 原博客地址 介绍 在ECharts中看到过这种圆形扩散效果,类似css3,刚好项目中想把它用上,but我又不想引入整个echart.js文件,更重要的是想弄明白它的原 ...
- 线性插值与矢量线性插值动画_看完这篇文章,不要再说不懂动画了
动画是WebGL的心脏.正是因为有了动画,一个静态的WebGL场景才会变成栩栩如生的真实体验,让场景更加生动形象.WebGL并没有太多预置制作动画的方法,然而Three.js拥有一些非常专业的动画库弥 ...
- java实心圆_html5使用canvas画空心圆与实心圆
摘要:这篇HTML5栏目下的"html5使用canvas画空心圆与实心圆",介绍的技术点是"canvas.Html5.空心.使用.与",希望对大家开发技术学习和 ...
- 用php绘制空心圆,html5使用canvas画空心圆与实心圆_html5教程技巧
这里给大家分享的是一个学习canvas的时候做的画空心圆与实心圆的练习题,非常简单. 代码如下: var canvas=document.getElementById("canvas&quo ...
- 手机号段对应地区编码_什么是编码器?了解两种类型的编码器
编码器提供反馈,以精确控制速度和位置.转子角位置是通过两个相差90度的方波和每转一圈发生的可选索引脉冲给出的.有多种技术可获得这些结果. 两种类型的编码器:光学和磁编码器 光学编码器 这种类型的编码器 ...
最新文章
- 2345电脑管家_极限挑战:同时安装4大国产杀毒软件,我的电脑是最安全的?
- Java多线程面试题通关手册!
- Pandas将dataframe保存为pickle文件并加载保存后的pickle文件查看dataframe数据实战
- python中文对齐_Python中英文对齐终极解决方案
- 第一篇:Django基础
- 牛客 - 「土」巨石滚滚(贪心)
- 使用RNN解决句子对匹配问题的常见网络结构
- 小程序 const moment = require('moment')_C++大作业-XXX管理程序
- 按照时间,每天分区;按照数字,200000一个分区
- 2数据库表增加一个字段_14个实用的数据库设计技巧!
- 一些服务器客户端的c例子
- 忘记mysql管理密码怎么办?
- linux(Ubuntu)系统解决校园网锐捷客户端联网问题
- 前端新技术(离线缓存、CDN内容分发网络)
- javascript中的字符串编码转换
- 微信摇一摇效果HTML,JavaScript+H5实现微信摇一摇功能
- 兰州计算机非全日制硕士有哪些学校,兰州大学2018年非全日制热门学科专业有哪些...
- VOT 2015 Benchmark 使用教程
- 从事IT业一个8年老兵转行前的自我总结2——从《易经》说开来
- 服务器匹配原理,王者荣耀实现原理学习笔记