New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Canvas and TimelineView to DOM renderer #449
Conversation
storage: .ellipse( | ||
.init( | ||
x: rect.origin | ||
.x + (rect.width > rect.height ? (rect.width / 2) - (rect.width - rect.height) : 0), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add some comments in code explaining why this change is needed?
} | ||
} | ||
|
||
private func draw() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would there be an easy way to split this into multiple functions to make it more readable?
} | ||
} | ||
|
||
private func pushPath(_ path: Path, in canvasContext: JSObject) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto
canvas | ||
.font = | ||
.string( | ||
"\(style) \(variant) \(weight) \(size ?? "17")pt \(lineHeight) \(family ?? Font.Design.default.families.joined(separator: " "))" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"\(style) \(variant) \(weight) \(size ?? "17")pt \(lineHeight) \(family ?? Font.Design.default.families.joined(separator: " "))" | |
""" | |
\(style) \(variant) \(weight) \(size ?? "17")pt \(lineHeight) \( | |
family ?? Font.Design.default.families.joined(separator: " ") | |
) | |
""" |
return .string(AnyColorBox.ResolvedValue( | ||
red: color.red, | ||
green: color.green, | ||
blue: color.blue, | ||
opacity: color.opacity * Double(opacity), | ||
space: color.space | ||
).cssValue) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this could moved into some extension on AnyColorBox.ResolvedValue
that has an initializer taking a color
value and supplied opacity
. That would reduce these 6 lines to 1 with just that helper initializer call, also potentially resolving the linter warning.
} | ||
} | ||
|
||
private extension GraphicsContext.Shading { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extensions below this line could into a separate file? Yes, they would no longer stay private
, but it would potentially improve readability and overall navigation through the project, preventing files from becoming larger.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Marvellous! 🎊
Other than a few linter nits, maybe there's a way to add an HTML snapshot test? Albeit primitive it could ensure that at least the <canvas>
tag is rendered.
Disregard the snapshot test comment, I realize this won't be possible right now because we can't test |
Haven't done much cleanup yet, but did add support for symbols: Canvas { context, size in
// Draw axes on the canvas.
context.stroke(Rectangle().path(in: .init(x: size.width / 2, y: 0, width: 1, height: size.height)), with: .color(.green))
context.stroke(Rectangle().path(in: .init(x: 0, y: size.height / 2, width: size.width, height: 1)), with: .color(.green))
// Resolve and draw the symbols, looking them up by tag.
guard let aSymbol = context.resolveSymbol(id: "a"),
let bSymbol = context.resolveSymbol(id: "b") else { return }
context.draw(aSymbol, at: .init(x: size.width / 2, y: size.height / 2), anchor: .topLeading)
context.draw(bSymbol, at: .init(x: size.width / 2, y: size.height / 2), anchor: .bottomTrailing)
} symbols: {
Text("A")
.frame(width: 100, height: 100)
.background(Color.red)
.cornerRadius(10)
.tag("a") // Tagged with "a"
Text("B")
.frame(width: 100, height: 100)
.background(Color.blue)
.tag("b") // Tagged with "b"
} To look up Views by their tag, I added support for private struct SymbolResolverLayout<ID: Hashable>: _VariadicView.ViewRoot {
let id: ID
func body(children: _VariadicView.Children) -> some View {
ForEach(children) {
if case let .tagged(tag) = $0[TagValueTraitKey<ID>.self],
tag == id
{
$0
}
}
}
}
// Then used like so:
_VariadicView.Tree(SymbolResolverLayout(id: id)) {
_storage.symbols
} The views are rendered in the canvas using SVG+foreignObject. |
Off-topic, but Picker("Choose...", selection: $selection) {
ForEach(items) { Text($0.title) }
Text("Clear").tag(Item.clear)
} or for re-tagging: Picker("Choose...", selection: $value) {
ForEach(items) {
Text($0.title)
.tag($0.value)
}
} |
Would this be ready for review soon? It would be great to keep the PR relatively small, and then additional features could be implemented in a separate PR, for easier review, and to have separate commits for such features in the |
Yes, it can be reviewed now. |
Sources/TokamakDemo/CanvasDemo.swift
Outdated
@@ -0,0 +1,96 @@ | |||
// Copyright 2019-2020 Tokamak contributors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Copyright 2019-2020 Tokamak contributors | |
// Copyright 2021 Tokamak contributors |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great! Just a few nits
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. 👍
This is built on the HTML canvas, and is compatible with the iOS 15
Canvas
view.TimelineView
with an.animation
schedule only runs withCanvas
at the moment with a special implementation usingrequestAnimationFrame
, but could be adjusted to work with other views as well.Here's a demo of confetti using
Canvas
:CanvasDemo.mov