mirror of
https://github.com/exyte/Macaw.git
synced 2024-10-05 16:57:12 +03:00
Merge pull request #693 from f3dm76/task/path-animation-with-example
Add path animation
This commit is contained in:
commit
bdc3b499c1
@ -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.strokeVar.end.animation(to: 1))
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
@ -580,6 +580,8 @@
|
||||
5B7E79CF20CBE69700C50BCF /* masking-path-02-b-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79CD20CBE69700C50BCF /* masking-path-02-b-manual.svg */; };
|
||||
5B7E79DE20D2781A00C50BCF /* masking-intro-01-f-manual.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79DC20D2781A00C50BCF /* masking-intro-01-f-manual.reference */; };
|
||||
5B7E79DF20D2781A00C50BCF /* masking-intro-01-f-manual.svg in Resources */ = {isa = PBXBuildFile; fileRef = 5B7E79DD20D2781A00C50BCF /* masking-intro-01-f-manual.svg */; };
|
||||
5B9B970C2486506A00CAF2CE /* PathAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BECDC7A222FE6DD009C8E6A /* PathAnimation.swift */; };
|
||||
5B9B970D2486506C00CAF2CE /* PathAnimationGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BECDC7C222FE6E2009C8E6A /* PathAnimationGenerator.swift */; };
|
||||
5BAE201F208E1211006BF277 /* SVGCanvas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5BAE201E208E1211006BF277 /* SVGCanvas.swift */; };
|
||||
5BAE2038208E163D006BF277 /* polyline.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5BAE2022208E1637006BF277 /* polyline.reference */; };
|
||||
5BAE2039208E163D006BF277 /* polygon.reference in Resources */ = {isa = PBXBuildFile; fileRef = 5BAE2023208E1637006BF277 /* polygon.reference */; };
|
||||
@ -601,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 */; };
|
||||
@ -1192,6 +1196,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>"; };
|
||||
@ -1573,6 +1579,7 @@
|
||||
57E5E1011E3B393900D1CB28 /* ContentsAnimation.swift */,
|
||||
57E5E1021E3B393900D1CB28 /* MorphingAnimation.swift */,
|
||||
57E5E1031E3B393900D1CB28 /* OpacityAnimation.swift */,
|
||||
5BECDC7C222FE6E2009C8E6A /* PathAnimationGenerator.swift */,
|
||||
57A27BD01E44C5460057BD3A /* ShapeAnimation.swift */,
|
||||
57E5E1041E3B393900D1CB28 /* TransformAnimation.swift */,
|
||||
);
|
||||
@ -1583,12 +1590,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>";
|
||||
@ -2763,6 +2771,7 @@
|
||||
57614B461F83D15600875933 /* ContentsAnimation.swift in Sources */,
|
||||
57614B471F83D15600875933 /* TouchEvent.swift in Sources */,
|
||||
57614B481F83D15600875933 /* MBezierPath+Extension_macOS.swift in Sources */,
|
||||
5B9B970C2486506A00CAF2CE /* PathAnimation.swift in Sources */,
|
||||
5BFEF5CF20B80A83008DAC11 /* BlendEffect.swift in Sources */,
|
||||
30FF496D215CF27E00FF653C /* MCAShapeLayerLineCap_macOS.swift in Sources */,
|
||||
57614B491F83D15600875933 /* MView_macOS.swift in Sources */,
|
||||
@ -2798,6 +2807,7 @@
|
||||
A7B47E44230EA402009DD7E5 /* Graphics_iOS.swift in Sources */,
|
||||
57614B611F83D15600875933 /* PanEvent.swift in Sources */,
|
||||
57614B621F83D15600875933 /* RotateEvent.swift in Sources */,
|
||||
5B9B970D2486506C00CAF2CE /* PathAnimationGenerator.swift in Sources */,
|
||||
57614B631F83D15600875933 /* Insets.swift in Sources */,
|
||||
3081E77E20DB58B100640F96 /* DescriptionExtensions.swift in Sources */,
|
||||
57614B641F83D15600875933 /* Rect.swift in Sources */,
|
||||
@ -2926,6 +2936,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 */,
|
||||
@ -2955,6 +2966,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
|
||||
}
|
||||
|
104
Source/animation/types/PathAnimation.swift
Normal file
104
Source/animation/types/PathAnimation.swift
Normal file
@ -0,0 +1,104 @@
|
||||
//
|
||||
// PathAnimation.swift
|
||||
// Macaw
|
||||
//
|
||||
// Created by Victor Sukochev on 01/01/2018.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class PathAnimation: AnimationImpl<Double> {
|
||||
|
||||
let isEnd: Bool = true
|
||||
|
||||
convenience init(animatedNode: Shape, isEnd: Bool, startValue: Double, finalValue: Double, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
|
||||
|
||||
let interpolationFunc = { (t: Double) -> Double in
|
||||
startValue.interpolate(finalValue, progress: t)
|
||||
}
|
||||
|
||||
self.init(animatedNode: animatedNode, isEnd: isEnd, valueFunc: interpolationFunc, animationDuration: animationDuration, delay: delay, autostart: autostart, fps: fps)
|
||||
}
|
||||
|
||||
init(animatedNode: Shape, isEnd: Bool, valueFunc: @escaping (Double) -> Double, animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
|
||||
super.init(observableValue: AnimatableVariable<Double>(isEnd ? animatedNode.strokeVar.end.value : animatedNode.strokeVar.start.value), valueFunc: valueFunc, animationDuration: animationDuration, delay: delay, fps: fps)
|
||||
type = .path
|
||||
node = animatedNode
|
||||
|
||||
if autostart {
|
||||
self.play()
|
||||
}
|
||||
}
|
||||
|
||||
init(animatedNode: Shape, isEnd: Bool, factory: @escaping (() -> ((Double) -> Double)), animationDuration: Double, delay: Double = 0.0, autostart: Bool = false, fps: UInt = 30) {
|
||||
super.init(observableValue: AnimatableVariable<Double>(isEnd ? animatedNode.strokeVar.end.value : animatedNode.strokeVar.start.value), 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<Double>
|
||||
|
||||
open class StrokeAnimatableVariable: AnimatableVariable<Stroke?> {
|
||||
|
||||
public var end: StrokeSideVariable {
|
||||
return StrokeSideVariable(parentVar: self, isEnd: true)
|
||||
}
|
||||
|
||||
public var start: StrokeSideVariable {
|
||||
return StrokeSideVariable(parentVar: self, isEnd: false)
|
||||
}
|
||||
}
|
||||
|
||||
open class StrokeSideVariable {
|
||||
|
||||
let parentVar: StrokeAnimatableVariable
|
||||
let isEnd: Bool
|
||||
var value: Double = 0
|
||||
|
||||
var node: Node? {
|
||||
parentVar.node
|
||||
}
|
||||
|
||||
init(parentVar: StrokeAnimatableVariable, isEnd: Bool, value: Double = 0) {
|
||||
self.parentVar = parentVar
|
||||
self.isEnd = isEnd
|
||||
self.value = value
|
||||
}
|
||||
|
||||
public func animate(_ desc: PathAnimationDescription) {
|
||||
_ = PathAnimation(animatedNode: node as! Shape, isEnd: isEnd, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: true)
|
||||
}
|
||||
|
||||
public func animation(_ desc: PathAnimationDescription) -> Animation {
|
||||
return PathAnimation(animatedNode: node as! Shape, isEnd: isEnd, valueFunc: desc.valueFunc, animationDuration: desc.duration, delay: desc.delay, autostart: false)
|
||||
}
|
||||
|
||||
public func animate(from: Double? = nil, to: Double = 1, during: Double = 1.0, delay: Double = 0.0) {
|
||||
self.animate(((from ?? 0) >> to).t(during, delay: delay))
|
||||
}
|
||||
|
||||
public func animation(from: Double? = nil, to: Double = 1, during: Double = 1.0, delay: Double = 0.0) -> Animation {
|
||||
if let safeFrom = from {
|
||||
return self.animation((safeFrom >> to).t(during, delay: delay))
|
||||
}
|
||||
let origin = Double(0)
|
||||
let factory = { () -> (Double) -> Double in
|
||||
{ (t: Double) in origin.interpolate(to, progress: t) }
|
||||
}
|
||||
return PathAnimation(animatedNode: node as! Shape, isEnd: isEnd, factory: factory, animationDuration: during, delay: delay)
|
||||
}
|
||||
|
||||
public func animation(_ f: @escaping ((Double) -> Double), during: Double = 1.0, delay: Double = 0.0) -> Animation {
|
||||
return PathAnimation(animatedNode: node as! Shape, isEnd: isEnd, valueFunc: f, animationDuration: during, delay: delay)
|
||||
}
|
||||
}
|
@ -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,96 @@
|
||||
//
|
||||
// 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 pathAnimation = animation as? PathAnimation, let shape = animation.node as? Shape, let renderer = animation.nodeRenderer else {
|
||||
return
|
||||
}
|
||||
|
||||
let layer = AnimationUtils.layerForNodeRenderer(renderer, animation: animation, shouldRenderContent: false)
|
||||
|
||||
// Creating proper animation
|
||||
let generatedAnim = generatePathAnimation(
|
||||
pathAnimation.getVFunc(),
|
||||
duration: animation.getDuration(),
|
||||
offset: animation.pausedProgress,
|
||||
fps: pathAnimation.logicalFps)
|
||||
|
||||
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(_ valueFunc: (Double) -> Double, duration: Double, offset: Double, fps: UInt) -> CAAnimation {
|
||||
|
||||
var strokeEndValues = [Double]()
|
||||
var timeValues = [Double]()
|
||||
|
||||
let step = 1.0 / (duration * Double(fps))
|
||||
|
||||
var dt = 0.0
|
||||
var tValue = Array(stride(from: 0.0, to: 1.0, by: step))
|
||||
tValue.append(1.0)
|
||||
for t in tValue {
|
||||
|
||||
dt = t
|
||||
if 1.0 - dt < step {
|
||||
dt = 1.0
|
||||
}
|
||||
|
||||
let value = valueFunc(offset + dt)
|
||||
strokeEndValues.append(value)
|
||||
timeValues.append(dt)
|
||||
}
|
||||
|
||||
let animation = CAKeyframeAnimation(keyPath: "strokeEnd")
|
||||
animation.fillMode = MCAMediaTimingFillMode.forwards
|
||||
animation.isRemovedOnCompletion = false
|
||||
|
||||
animation.duration = duration
|
||||
animation.values = strokeEndValues
|
||||
animation.keyTimes = timeValues as [NSNumber]?
|
||||
|
||||
return animation
|
||||
}
|
@ -90,31 +90,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)
|
||||
|
@ -18,7 +18,7 @@ open class Shape: Node {
|
||||
set(val) { fillVar.value = val }
|
||||
}
|
||||
|
||||
public let strokeVar: AnimatableVariable<Stroke?>
|
||||
public let strokeVar: StrokeAnimatableVariable
|
||||
open var stroke: Stroke? {
|
||||
get { return strokeVar.value }
|
||||
set(val) { strokeVar.value = val }
|
||||
@ -27,7 +27,7 @@ open class Shape: Node {
|
||||
public init(form: Locus, fill: Fill? = nil, stroke: Stroke? = nil, place: Transform = Transform.identity, opaque: Bool = true, opacity: Double = 1, clip: Locus? = nil, mask: Node? = nil, effect: Effect? = nil, visible: Bool = true, tag: [String] = []) {
|
||||
self.formVar = AnimatableVariable<Locus>(form)
|
||||
self.fillVar = AnimatableVariable<Fill?>(fill)
|
||||
self.strokeVar = AnimatableVariable<Stroke?>(stroke)
|
||||
self.strokeVar = StrokeAnimatableVariable(stroke)
|
||||
super.init(
|
||||
place: place,
|
||||
opaque: opaque,
|
||||
@ -40,8 +40,8 @@ open class Shape: Node {
|
||||
)
|
||||
|
||||
self.formVar.node = self
|
||||
self.strokeVar.node = self
|
||||
self.fillVar.node = self
|
||||
self.strokeVar.node = self
|
||||
}
|
||||
|
||||
override open var bounds: Rect? {
|
||||
|
@ -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