mirror of
https://github.com/exyte/Macaw.git
synced 2024-09-11 05:05:23 +03:00
Add path animation
This commit is contained in:
parent
b13d043073
commit
517e33e2cb
@ -17,6 +17,7 @@
|
||||
57AF398C1E67E9DB00F0BFE2 /* EventsExampleController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 57AF398B1E67E9DB00F0BFE2 /* EventsExampleController.swift */; };
|
||||
58E4D50C1D841C6E00EC8815 /* TransformExampleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E4D50B1D841C6E00EC8815 /* TransformExampleView.swift */; };
|
||||
5B195EAD2276D5C40008AE8B /* AnimationsHierarchyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B195EAB2276D5C40008AE8B /* AnimationsHierarchyViewController.swift */; };
|
||||
5B5B393B2481094200753058 /* PathAnimationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5B5B393A2481094200753058 /* PathAnimationView.swift */; };
|
||||
5BAE3CB120C54E3D006BEF51 /* FiltersViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAE3CB020C54E3D006BEF51 /* FiltersViewController.swift */; };
|
||||
66AE19DB1CC8CB3C00B78B5E /* tiger.svg in Resources */ = {isa = PBXBuildFile; fileRef = 66AE19DA1CC8CB3C00B78B5E /* tiger.svg */; };
|
||||
B02E75F11C16104900D1971D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = B02E75F01C16104900D1971D /* AppDelegate.swift */; };
|
||||
@ -52,6 +53,7 @@
|
||||
57AF398B1E67E9DB00F0BFE2 /* EventsExampleController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventsExampleController.swift; sourceTree = "<group>"; };
|
||||
58E4D50B1D841C6E00EC8815 /* TransformExampleView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransformExampleView.swift; sourceTree = "<group>"; };
|
||||
5B195EAB2276D5C40008AE8B /* AnimationsHierarchyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnimationsHierarchyViewController.swift; sourceTree = "<group>"; };
|
||||
5B5B393A2481094200753058 /* PathAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathAnimationView.swift; sourceTree = "<group>"; };
|
||||
5BAE3CB020C54E3D006BEF51 /* FiltersViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FiltersViewController.swift; sourceTree = "<group>"; };
|
||||
66AE19DA1CC8CB3C00B78B5E /* tiger.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; name = tiger.svg; path = Example/Assets/SVG/tiger.svg; sourceTree = "<group>"; };
|
||||
B02E75ED1C16104900D1971D /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
@ -105,6 +107,7 @@
|
||||
574EC4271CB7DE7F0063F317 /* Examples */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5B5B39392481092A00753058 /* PathAnimation */,
|
||||
49265C7E2227B1B200923A66 /* Text */,
|
||||
574EC4331CB7DE7F0063F317 /* Shapes */,
|
||||
6699B7CE1DFFE8B90072585E /* Transform */,
|
||||
@ -153,6 +156,14 @@
|
||||
path = Easing;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5B5B39392481092A00753058 /* PathAnimation */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5B5B393A2481094200753058 /* PathAnimationView.swift */,
|
||||
);
|
||||
path = PathAnimation;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5BAE3CAF20C54DA5006BEF51 /* Filters */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -374,6 +385,7 @@
|
||||
5B195EAD2276D5C40008AE8B /* AnimationsHierarchyViewController.swift in Sources */,
|
||||
58E4D50C1D841C6E00EC8815 /* TransformExampleView.swift in Sources */,
|
||||
B02E75F11C16104900D1971D /* AppDelegate.swift in Sources */,
|
||||
5B5B393B2481094200753058 /* PathAnimationView.swift in Sources */,
|
||||
5BAE3CB120C54E3D006BEF51 /* FiltersViewController.swift in Sources */,
|
||||
57AF398C1E67E9DB00F0BFE2 /* EventsExampleController.swift in Sources */,
|
||||
B04416FA1E041A420016BC50 /* EasingView.swift in Sources */,
|
||||
|
@ -1,11 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="pJi-Pa-uLB">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="pJi-Pa-uLB">
|
||||
<device id="retina4_7" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -14,7 +12,7 @@
|
||||
<objects>
|
||||
<navigationController title="Main Navigation Controller" id="pJi-Pa-uLB" sceneMemberID="viewController">
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" id="YMh-RQ-7Uo">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<connections>
|
||||
@ -38,10 +36,10 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="44" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="PrB-pf-YOR">
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
|
||||
<rect key="frame" x="0.0" y="44" width="375" height="623"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<view key="tableFooterView" contentMode="scaleToFill" id="05I-vn-LhR">
|
||||
<rect key="frame" x="0.0" y="72" width="375" height="44"/>
|
||||
<rect key="frame" x="0.0" y="100" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
@ -50,7 +48,7 @@
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="6q0-BY-xeB" id="CU1-RT-JgB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
@ -91,7 +89,7 @@
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="STd-gH-3Yz" customClass="AnimationsView" customModule="Example" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
</view>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="1cy-cc-TWk">
|
||||
@ -174,7 +172,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="yHP-DJ-SIl" customClass="SVGView" customModule="Macaw">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
<userDefinedRuntimeAttributes>
|
||||
<userDefinedRuntimeAttribute type="string" keyPath="fileName" value="tiger"/>
|
||||
@ -207,7 +205,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view multipleTouchEnabled="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="9JG-dg-RFE" customClass="MacawView" customModule="Macaw">
|
||||
<rect key="frame" x="0.0" y="64" width="375" height="603"/>
|
||||
<rect key="frame" x="0.0" y="44" width="375" height="623"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@ -241,7 +239,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="7vb-6r-3PQ" customClass="MorphingView" customModule="Example" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="128" width="375" height="539"/>
|
||||
<rect key="frame" x="0.0" y="108" width="375" height="559"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@ -272,7 +270,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="sIX-jt-2uP" customClass="ShapesExampleView" customModule="Example" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@ -287,7 +285,7 @@
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-143" y="470"/>
|
||||
<point key="canvasLocation" x="630" y="468"/>
|
||||
</scene>
|
||||
<!--Transform-->
|
||||
<scene sceneID="rdc-fb-hVp">
|
||||
@ -302,7 +300,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="UWr-5G-i0w" customClass="TransformExampleView" customModule="Example" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@ -332,7 +330,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleAspectFit" translatesAutoresizingMaskIntoConstraints="NO" id="8gR-K6-HAF" customClass="MacawView" customModule="Macaw">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@ -366,7 +364,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="hPm-Wd-j2e" customClass="TextsExampleView" customModule="Example" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@ -396,7 +394,7 @@
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="WpG-iW-4sM" customClass="MacawView" customModule="Macaw">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="647"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</subviews>
|
||||
@ -416,5 +414,35 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="598" y="1194"/>
|
||||
</scene>
|
||||
<!--PathAnimation-->
|
||||
<scene sceneID="a9H-hW-qGe">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="PathAnimationViewController" title="PathAnimation" automaticallyAdjustsScrollViewInsets="NO" id="ySx-nz-Rdw" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="d4G-8G-Gj8"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="e41-7I-vvw"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="iZo-2r-op4">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Bnp-nT-a9e" customClass="PathAnimationView" customModule="Example" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="Bnp-nT-a9e" firstAttribute="leading" secondItem="iZo-2r-op4" secondAttribute="leading" id="P5H-37-Y7J"/>
|
||||
<constraint firstItem="Bnp-nT-a9e" firstAttribute="top" secondItem="d4G-8G-Gj8" secondAttribute="bottom" id="cUK-Wp-aZO"/>
|
||||
<constraint firstItem="e41-7I-vvw" firstAttribute="top" secondItem="Bnp-nT-a9e" secondAttribute="bottom" id="wDz-Jc-MFE"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Bnp-nT-a9e" secondAttribute="trailing" id="zro-AD-Af6"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="Iwb-9g-Fir" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-143" y="470"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
|
@ -0,0 +1,88 @@
|
||||
//
|
||||
// PathAnimationView.swift
|
||||
// Example
|
||||
//
|
||||
// Created by Alisa Mylnikova on 29/05/2020.
|
||||
// Copyright © 2020 Exyte. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Macaw
|
||||
|
||||
class PathAnimationView: MacawView {
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(node: Group(), coder: aDecoder)
|
||||
|
||||
newScene()
|
||||
}
|
||||
|
||||
func newScene() {
|
||||
let side = Double(150)
|
||||
let x = Double(self.bounds.width/2) - side/2
|
||||
let y = Double(self.bounds.height/2) - side/2
|
||||
let initialTriangle = Shape(form: makeInitialTriangle(side: side).cgPath.toMacaw(), stroke: Stroke(fill: randomEmeraldColor(), width: 1), place: .move(x, y))
|
||||
|
||||
self.node = [initialTriangle].group()
|
||||
fractalStep(allTriangles: [initialTriangle], currentTier: [initialTriangle], side: side, depth: 0)
|
||||
}
|
||||
|
||||
func fractalStep(allTriangles: [Shape], currentTier: [Shape], side: Double, depth: Int) {
|
||||
var tierAnimations = [Animation]()
|
||||
for shape in currentTier {
|
||||
tierAnimations.append(shape.formVar.appearanceAnimation())
|
||||
}
|
||||
tierAnimations.combine().onComplete {
|
||||
if depth < 4 {
|
||||
let newTier = self.createTier(parentTier: currentTier, side: side/2)
|
||||
self.node = (allTriangles + newTier).group()
|
||||
self.fractalStep(allTriangles: allTriangles + newTier, currentTier: newTier, side: side/2, depth: depth+1)
|
||||
}
|
||||
}
|
||||
.play()
|
||||
}
|
||||
|
||||
func createTier(parentTier: [Shape], side: Double) -> [Shape] {
|
||||
let a = sqrt(3)/2
|
||||
let pointLeft = CGPoint(x: 0, y: side*a)
|
||||
let pointRight = CGPoint(x: side, y: side*a)
|
||||
let pointUp = CGPoint(x: side/2, y: 0)
|
||||
|
||||
let leftTriangle = makeTriangle(pointRight, pointUp, pointLeft)
|
||||
let rightTriangle = makeTriangle(pointLeft, pointRight, pointUp)
|
||||
let bottomTriangle = makeTriangle(pointUp, pointLeft, pointRight)
|
||||
|
||||
var result: [Shape] = []
|
||||
for parent in parentTier {
|
||||
let left = Shape(form: leftTriangle.cgPath.toMacaw(), stroke: Stroke(fill: randomEmeraldColor(), width: 1), place: parent.place.move(-side/2, 0))
|
||||
let right = Shape(form: rightTriangle.cgPath.toMacaw(), stroke: Stroke(fill: randomEmeraldColor(), width: 1), place: parent.place.move(3*side/2, 0))
|
||||
let bottom = Shape(form: bottomTriangle.cgPath.toMacaw(), stroke: Stroke(fill: randomEmeraldColor(), width: 1), place: parent.place.move(side/2, side*sqrt(3)))
|
||||
result.append(contentsOf: [left, right, bottom])
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func makeTriangle(_ point1: CGPoint, _ point2: CGPoint, _ point3: CGPoint) -> MBezierPath {
|
||||
let path = MBezierPath()
|
||||
path.move(to: point1)
|
||||
path.addLine(to: point2)
|
||||
path.addLine(to: point3)
|
||||
path.close()
|
||||
return path
|
||||
}
|
||||
|
||||
func makeInitialTriangle(side: Double) -> MBezierPath {
|
||||
let a = sqrt(3)/2
|
||||
let pointLeft = CGPoint(x: 0, y: side*a)
|
||||
let pointRight = CGPoint(x: side, y: side*a)
|
||||
let pointUp = CGPoint(x: side/2, y: 0)
|
||||
|
||||
return makeTriangle(pointRight, pointUp, pointLeft)
|
||||
}
|
||||
|
||||
func randomEmeraldColor() -> Color {
|
||||
return Color.rgb(r: Int.random(in: 70...100), g: Int.random(in: 190...220), b: Int.random(in: 110...140))
|
||||
}
|
||||
|
||||
}
|
@ -14,7 +14,8 @@ open class MenuViewController: UIViewController, UITableViewDataSource, UITableV
|
||||
"EventsExampleController",
|
||||
"FiltersViewController",
|
||||
"TextsViewController",
|
||||
"AnimationsHierarchyViewController"
|
||||
"AnimationsHierarchyViewController",
|
||||
"PathAnimationViewController"
|
||||
].map {
|
||||
UIStoryboard(name: "Main", bundle: .none).instantiateViewController(withIdentifier: $0)
|
||||
}
|
||||
|
@ -603,6 +603,8 @@
|
||||
5BAEA9C9206CEAA20049AAAE /* viewBox.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5BAEA9C8206CEAA20049AAAE /* viewBox.svg */; };
|
||||
5BC2CA0D21C7B8F500AC46D9 /* CombinationAnimationGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */; };
|
||||
5BC2CA0E21C7B8F900AC46D9 /* CombinationAnimationGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */; };
|
||||
5BECDC7B222FE6DE009C8E6A /* PathAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BECDC7A222FE6DD009C8E6A /* PathAnimation.swift */; };
|
||||
5BECDC7D222FE6E2009C8E6A /* PathAnimationGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BECDC7C222FE6E2009C8E6A /* PathAnimationGenerator.swift */; };
|
||||
5BFEF5CE20B80A83008DAC11 /* BlendEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFEF5CC20B80A82008DAC11 /* BlendEffect.swift */; };
|
||||
5BFEF5CF20B80A83008DAC11 /* BlendEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFEF5CC20B80A82008DAC11 /* BlendEffect.swift */; };
|
||||
5BFEF5D020B80A83008DAC11 /* ColorMatrixEffect.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BFEF5CD20B80A83008DAC11 /* ColorMatrixEffect.swift */; };
|
||||
@ -1195,6 +1197,8 @@
|
||||
5BAE2057208F24DE006BF277 /* SceneSerialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SceneSerialization.swift; sourceTree = "<group>"; };
|
||||
5BAEA9C8206CEAA20049AAAE /* viewBox.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = viewBox.svg; sourceTree = "<group>"; };
|
||||
5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombinationAnimationGenerator.swift; sourceTree = "<group>"; };
|
||||
5BECDC7A222FE6DD009C8E6A /* PathAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PathAnimation.swift; path = Source/animation/types/PathAnimation.swift; sourceTree = SOURCE_ROOT; };
|
||||
5BECDC7C222FE6E2009C8E6A /* PathAnimationGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PathAnimationGenerator.swift; path = animation_generators/PathAnimationGenerator.swift; sourceTree = "<group>"; };
|
||||
5BFEF5CC20B80A82008DAC11 /* BlendEffect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BlendEffect.swift; sourceTree = "<group>"; };
|
||||
5BFEF5CD20B80A83008DAC11 /* ColorMatrixEffect.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ColorMatrixEffect.swift; sourceTree = "<group>"; };
|
||||
5BFEF5D420BC1C1E008DAC11 /* paths-data-18-f-manual.svg */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "paths-data-18-f-manual.svg"; sourceTree = "<group>"; };
|
||||
@ -1576,6 +1580,7 @@
|
||||
57E5E1011E3B393900D1CB28 /* ContentsAnimation.swift */,
|
||||
57E5E1021E3B393900D1CB28 /* MorphingAnimation.swift */,
|
||||
57E5E1031E3B393900D1CB28 /* OpacityAnimation.swift */,
|
||||
5BECDC7C222FE6E2009C8E6A /* PathAnimationGenerator.swift */,
|
||||
57A27BD01E44C5460057BD3A /* ShapeAnimation.swift */,
|
||||
57E5E1041E3B393900D1CB28 /* TransformAnimation.swift */,
|
||||
);
|
||||
@ -1586,12 +1591,13 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
57E5E0F61E3B393900D1CB28 /* Cache */,
|
||||
5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */,
|
||||
57E5E0FB1E3B393900D1CB28 /* MorphingGenerator.swift */,
|
||||
57A27BD21E44C5570057BD3A /* ShapeAnimationGenerator.swift */,
|
||||
57E5E0FC1E3B393900D1CB28 /* OpacityGenerator.swift */,
|
||||
5BECDC7A222FE6DD009C8E6A /* PathAnimation.swift */,
|
||||
57A27BD21E44C5570057BD3A /* ShapeAnimationGenerator.swift */,
|
||||
5B7D7ED221300D4A00B5ED00 /* TimingFunction.swift */,
|
||||
57E5E0FE1E3B393900D1CB28 /* TransformGenerator.swift */,
|
||||
5BC2CA0C21C7B8F400AC46D9 /* CombinationAnimationGenerator.swift */,
|
||||
);
|
||||
path = animation_generators;
|
||||
sourceTree = "<group>";
|
||||
@ -2931,6 +2937,7 @@
|
||||
57E5E1AF1E3B393900D1CB28 /* CAAnimationClosure.swift in Sources */,
|
||||
5B6E194320AC58F900454E7E /* Gradient.swift in Sources */,
|
||||
A718CD441F45C28200966E06 /* Common_iOS.swift in Sources */,
|
||||
5BECDC7D222FE6E2009C8E6A /* PathAnimationGenerator.swift in Sources */,
|
||||
5B6E194120AC58F900454E7E /* Color.swift in Sources */,
|
||||
A718CD4D1F45C28F00966E06 /* Common_macOS.swift in Sources */,
|
||||
57F1087A1F53C92000DC365B /* MDisplayLink.swift in Sources */,
|
||||
@ -2961,6 +2968,7 @@
|
||||
5B6E193520AC58F900454E7E /* Stop.swift in Sources */,
|
||||
572CEFC71E2CED4B008C7C83 /* SWXMLHash+TypeConversion.swift in Sources */,
|
||||
30FF496B215CF0ED00FF653C /* MCAShapeLayerLineCap_iOS.swift in Sources */,
|
||||
5BECDC7B222FE6DE009C8E6A /* PathAnimation.swift in Sources */,
|
||||
57E5E16B1E3B393900D1CB28 /* AnimationSequence.swift in Sources */,
|
||||
5B1A8C7620A15F7300E5FFAE /* SVGNodeLayout.swift in Sources */,
|
||||
57E5E1671E3B393900D1CB28 /* MorphingGenerator.swift in Sources */,
|
||||
|
@ -17,6 +17,7 @@ enum AnimationType {
|
||||
case combine
|
||||
case morphing
|
||||
case shape
|
||||
case path
|
||||
case empty
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,12 @@ class AnimationProducer {
|
||||
self.play(next, context)
|
||||
}
|
||||
}
|
||||
case .path:
|
||||
addPathAnimation(animation, context, sceneLayer: layer) {
|
||||
if let next = animation.next {
|
||||
self.play(next, context)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
69
Source/animation/types/PathAnimation.swift
Normal file
69
Source/animation/types/PathAnimation.swift
Normal file
@ -0,0 +1,69 @@
|
||||
//
|
||||
// PathAnimation.swift
|
||||
// Macaw
|
||||
//
|
||||
// Created by Victor Sukochev on 01/01/2018.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class PathAnimation: AnimationImpl<Locus> {
|
||||
convenience init(animatedNode: Shape, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
|
||||
|
||||
let interpolationFunc = { (t: Double) -> Locus in
|
||||
return animatedNode.form
|
||||
}
|
||||
|
||||
self.init(animatedNode: animatedNode, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
|
||||
}
|
||||
|
||||
init(animatedNode: Shape, valueFunc: @escaping (Double) -> Locus, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
|
||||
super.init(observableValue: animatedNode.formVar, valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
|
||||
type = .path
|
||||
node = animatedNode
|
||||
|
||||
if autostart {
|
||||
self.play()
|
||||
}
|
||||
}
|
||||
|
||||
init(animatedNode: Shape, factory: @escaping (() -> ((Double) -> Locus)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
|
||||
super.init(observableValue: animatedNode.formVar, factory: factory, animationDuration: animationDuration, delay: delay, fps: fps)
|
||||
type = .path
|
||||
node = animatedNode
|
||||
|
||||
if autostart {
|
||||
self.play()
|
||||
}
|
||||
}
|
||||
|
||||
// Pause state not available for discreet animation
|
||||
override public func pause() {
|
||||
stop()
|
||||
}
|
||||
}
|
||||
|
||||
public typealias PathAnimationDescription = AnimationDescription<Locus>
|
||||
|
||||
public extension AnimatableVariable where T: LocusInterpolation {
|
||||
func appearanceAnimation(during: Double = 1.0, delay: Double = 0.0) -> Animation {
|
||||
return PathAnimation(animatedNode: node as! Shape, animationDuration: during, delay: delay, autostart: false)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Group
|
||||
|
||||
public extension AnimatableVariable where T: ContentsInterpolation {
|
||||
func appearanceAnimation(during: Double = 1.0, delay: Double = 0.0) -> Animation {
|
||||
let group = node as! Group
|
||||
let shapes = group.contents.compactMap { $0 as? Shape }
|
||||
var animations = shapes.map { $0.formVar.appearanceAnimation(during: during, delay: delay) }
|
||||
|
||||
let groups = group.contents.compactMap { $0 as? Group }
|
||||
let groupAnimations = groups.map({ $0.contentsVar.appearanceAnimation(during: during, delay: delay) })
|
||||
|
||||
animations.append(contentsOf: groupAnimations)
|
||||
|
||||
return animations.combine(node: node)
|
||||
}
|
||||
}
|
@ -69,27 +69,7 @@ func addMorphingAnimation(_ animation: BasicAnimation, _ context: AnimationConte
|
||||
}
|
||||
|
||||
layer.path = fromLocus.toCGPath()
|
||||
|
||||
// Stroke
|
||||
if let stroke = shape.stroke {
|
||||
if let color = stroke.fill as? Color {
|
||||
layer.strokeColor = color.toCG()
|
||||
} else {
|
||||
layer.strokeColor = MColor.black.cgColor
|
||||
}
|
||||
|
||||
layer.lineWidth = CGFloat(stroke.width)
|
||||
layer.lineCap = MCAShapeLayerLineCap.mapToGraphics(model: stroke.cap)
|
||||
layer.lineJoin = MCAShapeLayerLineJoin.mapToGraphics(model: stroke.join)
|
||||
layer.lineDashPattern = stroke.dashes.map { NSNumber(value: $0) }
|
||||
}
|
||||
|
||||
// Fill
|
||||
if let color = shape.fill as? Color {
|
||||
layer.fillColor = color.toCG()
|
||||
} else {
|
||||
layer.fillColor = MColor.clear.cgColor
|
||||
}
|
||||
layer.setupStrokeAndFill(shape)
|
||||
|
||||
let animationId = animation.ID
|
||||
layer.add(generatedAnimation, forKey: animationId)
|
||||
|
@ -0,0 +1,73 @@
|
||||
//
|
||||
// PathAnimationGenerator.swift
|
||||
// Macaw
|
||||
//
|
||||
// Created by Victor Sukochev on 01/01/2018.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
#if os(iOS)
|
||||
import UIKit
|
||||
#elseif os(OSX)
|
||||
import AppKit
|
||||
#endif
|
||||
|
||||
func addPathAnimation(_ animation: BasicAnimation, _ context: AnimationContext, sceneLayer: CALayer, completion: @escaping (() -> Void)) {
|
||||
|
||||
guard let shape = animation.node as? Shape, let renderer = animation.nodeRenderer else {
|
||||
return
|
||||
}
|
||||
|
||||
let duration = animation.autoreverses ? animation.getDuration() / 2.0 : animation.getDuration()
|
||||
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false)
|
||||
|
||||
// Creating proper animation
|
||||
let generatedAnim = generatePathAnimation(from: 0.0, to: 1.0, duration: duration)
|
||||
|
||||
generatedAnim.repeatCount = Float(animation.repeatCount)
|
||||
generatedAnim.timingFunction = caTimingFunction(animation.easing)
|
||||
generatedAnim.autoreverses = animation.autoreverses
|
||||
|
||||
generatedAnim.progress = { progress in
|
||||
let t = Double(progress)
|
||||
|
||||
animation.progress = t
|
||||
animation.onProgressUpdate?(t)
|
||||
}
|
||||
|
||||
generatedAnim.completion = { finished in
|
||||
|
||||
animation.progress = animation.manualStop ? 0.0 : 1.0
|
||||
|
||||
renderer.freeLayer()
|
||||
|
||||
if !animation.cycled && !animation.manualStop {
|
||||
animation.completion?()
|
||||
}
|
||||
|
||||
completion()
|
||||
}
|
||||
|
||||
//layer.path = RenderUtils.toCGPath(shape.form).copy(using: &layer.transform)
|
||||
layer.path = shape.form.toCGPath()
|
||||
layer.setupStrokeAndFill(shape)
|
||||
|
||||
layer.add(generatedAnim, forKey: animation.ID)
|
||||
animation.removeFunc = { [weak layer] in
|
||||
layer?.removeAnimation(forKey: animation.ID)
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate func generatePathAnimation(from: Double, to: Double, duration: Double) -> CAAnimation {
|
||||
|
||||
let animation = CABasicAnimation(keyPath: "strokeEnd")
|
||||
animation.fromValue = from
|
||||
animation.toValue = to
|
||||
animation.duration = duration
|
||||
animation.fillMode = CAMediaTimingFillMode.forwards
|
||||
animation.isRemovedOnCompletion = false
|
||||
|
||||
return animation
|
||||
}
|
@ -76,31 +76,7 @@ func addShapeAnimation(_ animation: BasicAnimation, _ context: AnimationContext,
|
||||
}
|
||||
|
||||
layer.path = fromShape.form.toCGPath()
|
||||
|
||||
// Stroke
|
||||
if let stroke = shape.stroke {
|
||||
if let color = stroke.fill as? Color {
|
||||
layer.strokeColor = color.toCG()
|
||||
} else {
|
||||
layer.strokeColor = MColor.black.cgColor
|
||||
}
|
||||
|
||||
layer.lineWidth = CGFloat(stroke.width)
|
||||
layer.lineCap = MCAShapeLayerLineCap.mapToGraphics(model: stroke.cap)
|
||||
layer.lineJoin = MCAShapeLayerLineJoin.mapToGraphics(model: stroke.join)
|
||||
layer.lineDashPattern = stroke.dashes.map { NSNumber(value: $0) }
|
||||
layer.lineDashPhase = CGFloat(stroke.offset)
|
||||
} else if shape.fill == nil {
|
||||
layer.strokeColor = MColor.black.cgColor
|
||||
layer.lineWidth = 1.0
|
||||
}
|
||||
|
||||
// Fill
|
||||
if let color = shape.fill as? Color {
|
||||
layer.fillColor = color.toCG()
|
||||
} else {
|
||||
layer.fillColor = MColor.clear.cgColor
|
||||
}
|
||||
layer.setupStrokeAndFill(fromShape)
|
||||
|
||||
let animationId = animation.ID
|
||||
layer.add(generatedAnimation, forKey: animationId)
|
||||
|
@ -20,3 +20,35 @@ class ShapeLayer: CAShapeLayer {
|
||||
renderer?.directRender(in: ctx, force: isForceRenderingEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
extension ShapeLayer {
|
||||
|
||||
func setupStrokeAndFill(_ shape: Shape) {
|
||||
|
||||
// Stroke
|
||||
if let stroke = shape.stroke {
|
||||
if let color = stroke.fill as? Color {
|
||||
strokeColor = color.toCG()
|
||||
} else {
|
||||
strokeColor = MColor.black.cgColor
|
||||
}
|
||||
|
||||
lineWidth = CGFloat(stroke.width)
|
||||
lineCap = MCAShapeLayerLineCap.mapToGraphics(model: stroke.cap)
|
||||
lineJoin = MCAShapeLayerLineJoin.mapToGraphics(model: stroke.join)
|
||||
lineDashPattern = stroke.dashes.map { NSNumber(value: $0) }
|
||||
lineDashPhase = CGFloat(stroke.offset)
|
||||
} else if shape.fill == nil {
|
||||
strokeColor = MColor.black.cgColor
|
||||
lineWidth = 1.0
|
||||
}
|
||||
|
||||
// Fill
|
||||
if let color = shape.fill as? Color {
|
||||
fillColor = color.toCG()
|
||||
} else {
|
||||
fillColor = MColor.clear.cgColor
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user