Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions ci/release/changelogs/next.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#### Features 🚀

- exports: gif exports work with `animate: true` keyword [#2663](https://github.com/terrastruct/d2/pull/2663)
- animations:
- unidirectional connections with an icon and `animate: true` animate the icon [#2666](https://github.com/terrastruct/d2/pull/2666)
- unidirectional connections with no `stroke-dash` and `animate: true` animate the path growing [#2666](https://github.com/terrastruct/d2/pull/2666)

#### Improvements 🧹

Expand All @@ -13,6 +16,8 @@
- `animate-interval` is no longer required, defaults to 1000ms for gifs [#2663](https://github.com/terrastruct/d2/pull/2663)
- renders:
- remote images are fetched more reliably [#2659](https://github.com/terrastruct/d2/pull/2659)
- vars:
- `animate-interval` may be set as a `d2-config` variable [#2666](https://github.com/terrastruct/d2/pull/2666)

#### Bugfixes ⛑️

Expand Down
19 changes: 10 additions & 9 deletions d2cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,15 +337,16 @@ func Run(ctx context.Context, ms *xmain.State) (err error) {
}

renderOpts := d2svg.RenderOpts{
Pad: padFlag,
Sketch: sketchFlag,
Center: centerFlag,
ThemeID: themeFlag,
DarkThemeID: darkThemeFlag,
Scale: scale,
NoXMLTag: noXMLTagFlag,
Salt: saltFlag,
OmitVersion: omitVersionFlag,
Pad: padFlag,
Sketch: sketchFlag,
Center: centerFlag,
ThemeID: themeFlag,
DarkThemeID: darkThemeFlag,
Scale: scale,
NoXMLTag: noXMLTagFlag,
Salt: saltFlag,
OmitVersion: omitVersionFlag,
AnimateInterval: int(*animateIntervalFlag),
}

if *watchFlag {
Expand Down
6 changes: 6 additions & 0 deletions d2compiler/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,12 @@ func compileConfig(ir *d2ir.Map) (*d2target.Config, error) {
config.Center = &val
}

f = configMap.GetField(d2ast.FlatUnquotedString("animate-interval"))
if f != nil {
val, _ := strconv.Atoi(f.Primary().Value.ScalarString())
config.AnimateInterval = go2.Pointer(int64(val))
}

f = configMap.GetField(d2ast.FlatUnquotedString("theme-overrides"))
if f != nil {
overrides, err := compileThemeOverrides(f.Map())
Expand Down
2 changes: 1 addition & 1 deletion d2ir/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ func (c *compiler) validateConfigs(configs *Field) {
c.errorf(f.LastRef().AST(), `%d is not a valid theme ID`, valInt)
continue
}
case "pad":
case "pad", "animate-interval":
_, err := strconv.Atoi(val)
if err != nil {
c.errorf(f.LastRef().AST(), `expected an integer for "%s", got "%s"`, f.Name.ScalarString(), val)
Expand Down
90 changes: 51 additions & 39 deletions d2renderers/d2sketch/sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,48 +371,56 @@ func Paths(r jsrunner.JSRunner, shape d2target.Shape, diagramHash string, paths
}

func Connection(r jsrunner.JSRunner, connection d2target.Connection, path, attrs string) (string, error) {
animatedGrow := connection.Animated && connection.Icon == nil && connection.StrokeDash == 0 && !connection.IsBidirectional()
animatedClass := ""
if connection.Animated {
if connection.Animated && connection.Icon == nil && (connection.StrokeDash != 0 || connection.IsBidirectional()) {
animatedClass = " animated-connection"
} else if animatedGrow {
animatedClass = " animated-connection-grow"
}

if connection.Animated {
// If connection is animated and bidirectional
if (connection.DstArrow == d2target.NoArrowhead && connection.SrcArrow == d2target.NoArrowhead) || (connection.DstArrow != d2target.NoArrowhead && connection.SrcArrow != d2target.NoArrowhead) {
// There is no pure CSS way to animate bidirectional connections in two directions, so we split it up
path1, path2, err := svg.SplitPath(path, 0.5)
// If connection is animated and bidirectional
if connection.Animated && connection.Icon == nil && connection.IsBidirectional() {
// There is no pure CSS way to animate bidirectional connections in two directions, so we split it up
path1, path2, err := svg.SplitPath(path, 0.5)

if err != nil {
return "", err
}
if err != nil {
return "", err
}

pathEl1 := d2themes.NewThemableElement("path", nil)
pathEl1.D = path1
pathEl1.Fill = color.None
pathEl1.Stroke = connection.Stroke
pathEl1.ClassName = fmt.Sprintf("connection%s", animatedClass)
pathEl1.Style = connection.CSSStyle()
pathEl1.Style += "animation-direction: reverse;"
pathEl1.Attributes = attrs

pathEl2 := d2themes.NewThemableElement("path", nil)
pathEl2.D = path2
pathEl2.Fill = color.None
pathEl2.Stroke = connection.Stroke
pathEl2.ClassName = fmt.Sprintf("connection%s", animatedClass)
pathEl2.Style = connection.CSSStyle()
pathEl2.Attributes = attrs
return pathEl1.Render() + " " + pathEl2.Render(), nil
} else {
pathEl := d2themes.NewThemableElement("path", nil)
pathEl.D = path
pathEl.Fill = color.None
pathEl.Stroke = connection.Stroke
pathEl.ClassName = fmt.Sprintf("connection%s", animatedClass)
pathEl.Style = connection.CSSStyle()
pathEl.Attributes = attrs
return pathEl.Render(), nil
pathEl1 := d2themes.NewThemableElement("path", nil)
pathEl1.D = path1
pathEl1.Fill = color.None
pathEl1.Stroke = connection.Stroke
pathEl1.ClassName = fmt.Sprintf("connection%s", animatedClass)
pathEl1.Style = connection.CSSStyle()
pathEl1.Style += "animation-direction: reverse;"
pathEl1.Attributes = attrs

pathEl2 := d2themes.NewThemableElement("path", nil)
pathEl2.D = path2
pathEl2.Fill = color.None
pathEl2.Stroke = connection.Stroke
pathEl2.ClassName = fmt.Sprintf("connection%s", animatedClass)
pathEl2.Style = connection.CSSStyle()
pathEl2.Attributes = attrs
return pathEl1.Render() + " " + pathEl2.Render(), nil
} else if connection.Animated && connection.Icon == nil {
pathEl := d2themes.NewThemableElement("path", nil)
pathEl.D = path
pathEl.Fill = color.None
pathEl.Stroke = connection.Stroke
pathEl.ClassName = fmt.Sprintf("connection%s", animatedClass)
pathEl.Style = connection.CSSStyle()
if animatedGrow {
pathData := strings.Split(strings.TrimSpace(path), " ")
pathLen, err := svg.PathLength(pathData)
if err == nil {
pathEl.Style += fmt.Sprintf("stroke-dasharray:%f;stroke-dashoffset:%f;", pathLen, pathLen)
}
}
pathEl.Attributes = attrs
return pathEl.Render(), nil
} else {
roughness := 0.5
js := fmt.Sprintf(`node = rc.path("%s", {roughness: %f, seed: 1});`, path, roughness)
Expand Down Expand Up @@ -749,19 +757,23 @@ func computeRoughPaths(r jsrunner.JSRunner, js string) ([]roughPath, error) {
return extractRoughPaths(r)
}

type attrs struct {
func ComputeRoughPaths(r jsrunner.JSRunner, js string) ([]roughPath, error) {
return computeRoughPaths(r, js)
}

type Attrs struct {
D string `json:"d"`
}

type style struct {
type Style struct {
Stroke string `json:"stroke,omitempty"`
StrokeWidth string `json:"strokeWidth,omitempty"`
Fill string `json:"fill,omitempty"`
}

type roughPath struct {
Attrs attrs `json:"attrs"`
Style style `json:"style"`
Attrs Attrs `json:"attrs"`
Style Style `json:"style"`
}

func (rp roughPath) StyleCSS() string {
Expand Down
Loading
Loading