package d2animate import ( "bytes" "fmt" "math" "strings" "oss.terrastruct.com/d2/d2renderers/d2sketch" "oss.terrastruct.com/d2/d2renderers/d2svg" "oss.terrastruct.com/d2/d2target" "oss.terrastruct.com/d2/lib/version" ) var transitionDurationMS = 1 func makeKeyframe(delayMS, durationMS, totalMS, identifier int, diagramHash string) string { percentageBefore := (math.Max(0, float64(delayMS-transitionDurationMS)) / float64(totalMS)) * 100. percentageStart := (float64(delayMS) / float64(totalMS)) * 100. percentageEnd := (float64(delayMS+durationMS-transitionDurationMS) / float64(totalMS)) * 100. if int(math.Ceil(percentageEnd)) == 100 { return fmt.Sprintf(`@keyframes d2Transition-%s-%d { 0%%, %f%% { opacity: 0; } %f%%, %f%% { opacity: 1; } }`, diagramHash, identifier, percentageBefore, percentageStart, math.Ceil(percentageEnd)) } percentageAfter := (float64(delayMS+durationMS) / float64(totalMS)) * 100. return fmt.Sprintf(`@keyframes d2Transition-%s-%d { 0%%, %f%% { opacity: 0; } %f%%, %f%% { opacity: 1; } %f%%, 100%% { opacity: 0; } }`, diagramHash, identifier, percentageBefore, percentageStart, percentageEnd, percentageAfter) } func Wrap(rootDiagram *d2target.Diagram, svgs [][]byte, renderOpts d2svg.RenderOpts, intervalMS int) ([]byte, error) { buf := &bytes.Buffer{} // TODO account for stroke width of root border tl, br := rootDiagram.NestedBoundingBox() left := tl.X - int(*renderOpts.Pad) top := tl.Y - int(*renderOpts.Pad) width := br.X - tl.X + int(*renderOpts.Pad)*2 height := br.Y - tl.Y + int(*renderOpts.Pad)*2 fitToScreenWrapperOpening := fmt.Sprintf(``, version.Version, width, height, ) fmt.Fprint(buf, fitToScreenWrapperOpening) innerOpening := fmt.Sprintf(``, width, height, left, top, width, height) fmt.Fprint(buf, innerOpening) svgsStr := "" for _, svg := range svgs { svgsStr += string(svg) + " " } diagramHash, err := rootDiagram.HashID() if err != nil { return nil, err } d2svg.EmbedFonts(buf, diagramHash, svgsStr, rootDiagram.FontFamily, rootDiagram.GetNestedCorpus()) themeStylesheet, err := d2svg.ThemeCSS(diagramHash, renderOpts.ThemeID, renderOpts.DarkThemeID, renderOpts.ThemeOverrides, renderOpts.DarkThemeOverrides) if err != nil { return nil, err } fmt.Fprintf(buf, ``, d2svg.BaseStylesheet, themeStylesheet) if rootDiagram.HasShape(func(s d2target.Shape) bool { return s.Label != "" && s.Type == d2target.ShapeText }) { css := d2svg.MarkdownCSS css = strings.ReplaceAll(css, "font-italic", fmt.Sprintf("%s-font-italic", diagramHash)) css = strings.ReplaceAll(css, "font-bold", fmt.Sprintf("%s-font-bold", diagramHash)) css = strings.ReplaceAll(css, "font-mono", fmt.Sprintf("%s-font-mono", diagramHash)) css = strings.ReplaceAll(css, "font-regular", fmt.Sprintf("%s-font-regular", diagramHash)) fmt.Fprintf(buf, ``, css) } if renderOpts.Sketch != nil && *renderOpts.Sketch { d2sketch.DefineFillPatterns(buf) } fmt.Fprint(buf, ``) for i, svg := range svgs { str := string(svg) str = strings.Replace(str, "") fmt.Fprint(buf, "") return buf.Bytes(), nil }