Canvas refactoring

This commit is contained in:
anoshenko 2022-11-02 20:08:02 +03:00
parent b26525e892
commit 8200f98d0d
9 changed files with 471 additions and 508 deletions

View File

@ -4,6 +4,8 @@ package rui
import (
_ "embed"
"encoding/base64"
"path/filepath"
"strings"
"syscall/js"
)
@ -112,10 +114,46 @@ func (app *wasmApp) createSession() Session {
return session
}
func (app *wasmApp) init() {
func (app *wasmApp) init(params AppParams) {
app.params = params
document := js.Global().Get("document")
body := document.Call("querySelector", "body")
head := document.Call("querySelector", "head")
meta := document.Call("createElement", "meta")
meta.Set("name", "viewport")
meta.Set("content", "width=device-width")
head.Call("appendChild", meta)
meta = document.Call("createElement", "base")
meta.Set("target", "_blank")
meta.Set("rel", "noopener")
head.Call("appendChild", meta)
if params.Icon != "" {
if image, ok := resources.images[params.Icon]; ok && image.fs != nil {
dataType := map[string]string{
".svg": "data:image/svg+xml",
".png": "data:image/png",
".jpg": "data:image/jpg",
".jpeg": "data:image/jpg",
".gif": "data:image/gif",
}
ext := strings.ToLower(filepath.Ext(params.Icon))
if prefix, ok := dataType[ext]; ok {
if data, err := image.fs.ReadFile(image.path); err == nil {
meta = document.Call("createElement", "link")
meta.Set("rel", "icon")
meta.Set("href", prefix+";base64,"+base64.StdEncoding.EncodeToString(data))
head.Call("appendChild", meta)
} else {
DebugLog(err.Error())
}
}
}
}
script := document.Call("createElement", "script")
script.Set("type", "text/javascript")
@ -156,6 +194,11 @@ func (app *wasmApp) init() {
div.Set("download", "")
div.Set("style", "display: none;")
body.Call("appendChild", div)
if params.TitleColor != 0 {
app.brige.runFunc("setTitleColor", params.TitleColor.cssString())
}
}
// StartApp - create the new wasmApp and start it
@ -168,11 +211,10 @@ func StartApp(addr string, createContentFunc func(Session) SessionContent, param
}
app := new(wasmApp)
app.params = params
app.createContentFunc = createContentFunc
app.brige = createWasmBrige()
app.init()
app.init(params)
<-app.close
}

View File

@ -1796,3 +1796,16 @@ function getPropertyValue(answerID, elementId, name) {
function appendStyles(styles) {
document.querySelector('style').textContent += styles
}
function getCanvasContext(elementId) {
const canvas = document.getElementById(elementId)
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
//var gradient;
//var path;
//var img;
ctx.canvas.width = dpr * canvas.clientWidth;
ctx.canvas.height = dpr * canvas.clientHeight;
ctx.scale(dpr, dpr);
return ctx;
}

535
canvas.go
View File

@ -1,7 +1,7 @@
package rui
import (
"fmt"
"math"
"strconv"
"strings"
)
@ -290,42 +290,24 @@ type Canvas interface {
// in the rectangle (dstX, dstY, dstWidth, dstHeight), scaling in height and width if necessary
DrawImageFragment(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight float64, image Image)
finishDraw() string
finishDraw()
}
type canvasData struct {
view CanvasView
script strings.Builder
session Session
}
func newCanvas(view CanvasView) Canvas {
canvas := new(canvasData)
canvas.view = view
canvas.script.Grow(4096)
canvas.script.WriteString(`const canvas = document.getElementById('`)
canvas.script.WriteString(view.htmlID())
canvas.script.WriteString(`');
const ctx = canvas.getContext('2d');
const dpr = window.devicePixelRatio || 1;
var gradient;
var path;
var img;
ctx.canvas.width = dpr * canvas.clientWidth;
ctx.canvas.height = dpr * canvas.clientHeight;
ctx.scale(dpr, dpr);`)
/*
canvas.script.WriteString(strconv.FormatFloat(view.canvasWidth(), 'g', -1, 64))
canvas.script.WriteString(`;
ctx.canvas.height = dpr * `)
canvas.script.WriteString(strconv.FormatFloat(view.canvasHeight(), 'g', -1, 64))
canvas.script.WriteString(";\nctx.scale(dpr, dpr);")
*/
canvas.session = view.Session()
canvas.session.cavnasStart(view.htmlID())
return canvas
}
func (canvas *canvasData) finishDraw() string {
canvas.script.WriteString("\n")
return canvas.script.String()
func (canvas *canvasData) finishDraw() {
canvas.session.cavnasFinish()
}
func (canvas *canvasData) View() CanvasView {
@ -347,162 +329,102 @@ func (canvas *canvasData) Height() float64 {
}
func (canvas *canvasData) Save() {
canvas.script.WriteString("\nctx.save();")
canvas.session.callCanvasFunc("save")
}
func (canvas *canvasData) Restore() {
canvas.script.WriteString("\nctx.restore();")
canvas.session.callCanvasFunc("restore")
}
func (canvas *canvasData) ClipRect(x, y, width, height float64) {
canvas.script.WriteString("\nctx.beginPath();\nctx.rect(")
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(width, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(height, 'g', -1, 64))
canvas.script.WriteString(");\nctx.clip();")
canvas.session.callCanvasFunc("beginPath")
canvas.session.callCanvasFunc("rect", x, y, width, height)
canvas.session.callCanvasFunc("clip")
}
func (canvas *canvasData) ClipPath(path Path) {
canvas.script.WriteString(path.scriptText())
canvas.script.WriteString("\nctx.clip();")
path.create(canvas.session)
canvas.session.callCanvasFunc("clip")
}
func (canvas *canvasData) SetScale(x, y float64) {
canvas.script.WriteString("\nctx.scale(")
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("scale", x, y)
}
func (canvas *canvasData) SetTranslation(x, y float64) {
canvas.script.WriteString("\nctx.translate(")
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("translate", x, y)
}
func (canvas *canvasData) SetRotation(angle float64) {
canvas.script.WriteString("\nctx.rotate(")
canvas.script.WriteString(strconv.FormatFloat(angle, 'g', -1, 64))
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("rotate", angle)
}
func (canvas *canvasData) SetTransformation(xScale, yScale, xSkew, ySkew, dx, dy float64) {
canvas.script.WriteString("\nctx.transform(")
canvas.script.WriteString(strconv.FormatFloat(xScale, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(ySkew, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(xSkew, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(yScale, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(dx, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(dy, 'g', -1, 64))
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("transform", xScale, ySkew, xSkew, yScale, dx, dy)
}
func (canvas *canvasData) ResetTransformation() {
canvas.script.WriteString("\nctx.resetTransform();\nctx.scale(dpr, dpr);")
canvas.session.callCanvasFunc("resetTransform")
canvas.session.callCanvasFunc("scale", canvas.session.PixelRatio(), canvas.session.PixelRatio())
//canvas.session.callCanvasFunc("scale", angle)
// TODO canvas.script.WriteString("\nctx.resetTransform();\nctx.scale(dpr, dpr);")
}
func (canvas *canvasData) SetSolidColorFillStyle(color Color) {
canvas.script.WriteString("\nctx.fillStyle = \"")
canvas.script.WriteString(color.cssString())
canvas.script.WriteString(`";`)
canvas.session.updateCanvasProperty("fillStyle", color.cssString())
}
func (canvas *canvasData) SetSolidColorStrokeStyle(color Color) {
canvas.script.WriteString("\nctx.strokeStyle = \"")
canvas.script.WriteString(color.cssString())
canvas.script.WriteString(`";`)
canvas.session.updateCanvasProperty("strokeStyle", color.cssString())
}
func (canvas *canvasData) setLinearGradient(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint) {
canvas.script.WriteString("\ngradient = ctx.createLinearGradient(")
canvas.script.WriteString(strconv.FormatFloat(x0, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y0, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(x1, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y1, 'g', -1, 64))
canvas.script.WriteString(");\ngradient.addColorStop(0, '")
canvas.script.WriteString(color0.cssString())
canvas.script.WriteString("');")
func (canvas *canvasData) createLinearGradient(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint) any {
gradient := canvas.session.createCanvasVar("createLinearGradient", x0, y0, x1, y1)
canvas.session.callCanvasVarFunc(gradient, "addColorStop", 0, color0.cssString())
for _, point := range stopPoints {
if point.Offset >= 0 && point.Offset <= 1 {
canvas.script.WriteString("\ngradient.addColorStop(")
canvas.script.WriteString(strconv.FormatFloat(point.Offset, 'g', -1, 64))
canvas.script.WriteString(", '")
canvas.script.WriteString(point.Color.cssString())
canvas.script.WriteString("');")
canvas.session.callCanvasVarFunc(gradient, "addColorStop", point.Offset, point.Color.cssString())
}
}
canvas.script.WriteString("\ngradient.addColorStop(1, '")
canvas.script.WriteString(color1.cssString())
canvas.script.WriteString("');")
canvas.session.callCanvasVarFunc(gradient, "addColorStop", 1, color1.cssString())
return gradient
}
func (canvas *canvasData) SetLinearGradientFillStyle(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint) {
canvas.setLinearGradient(x0, y0, color0, x1, y1, color1, stopPoints)
canvas.script.WriteString("\nctx.fillStyle = gradient;")
gradient := canvas.createLinearGradient(x0, y0, color0, x1, y1, color1, stopPoints)
canvas.session.updateCanvasProperty("fillStyle", gradient)
}
func (canvas *canvasData) SetLinearGradientStrokeStyle(x0, y0 float64, color0 Color, x1, y1 float64, color1 Color, stopPoints []GradientPoint) {
canvas.setLinearGradient(x0, y0, color0, x1, y1, color1, stopPoints)
canvas.script.WriteString("\nctx.strokeStyle = gradient;")
gradient := canvas.createLinearGradient(x0, y0, color0, x1, y1, color1, stopPoints)
canvas.session.updateCanvasProperty("strokeStyle", gradient)
}
func (canvas *canvasData) setRadialGradient(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint) {
canvas.script.WriteString("\ngradient = ctx.createRadialGradient(")
canvas.script.WriteString(strconv.FormatFloat(x0, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y0, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(r0, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(x1, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y1, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(r1, 'g', -1, 64))
canvas.script.WriteString(");\ngradient.addColorStop(0, '")
canvas.script.WriteString(color0.cssString())
canvas.script.WriteString("');")
func (canvas *canvasData) createRadialGradient(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint) any {
gradient := canvas.session.createCanvasVar("createRadialGradient", x0, y0, r0, x1, y1, r1)
canvas.session.callCanvasVarFunc(gradient, "addColorStop", 0, color0.cssString())
for _, point := range stopPoints {
if point.Offset >= 0 && point.Offset <= 1 {
canvas.script.WriteString("\ngradient.addColorStop(")
canvas.script.WriteString(strconv.FormatFloat(point.Offset, 'g', -1, 64))
canvas.script.WriteString(", '")
canvas.script.WriteString(point.Color.cssString())
canvas.script.WriteString("');")
canvas.session.callCanvasVarFunc(gradient, "addColorStop", point.Offset, point.Color.cssString())
}
}
canvas.script.WriteString("\ngradient.addColorStop(1, '")
canvas.script.WriteString(color1.cssString())
canvas.script.WriteString("');")
canvas.session.callCanvasVarFunc(gradient, "addColorStop", 1, color1.cssString())
return gradient
}
func (canvas *canvasData) SetRadialGradientFillStyle(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint) {
canvas.setRadialGradient(x0, y0, r0, color0, x1, y1, r1, color1, stopPoints)
canvas.script.WriteString("\nctx.fillStyle = gradient;")
gradient := canvas.createRadialGradient(x0, y0, r0, color0, x1, y1, r1, color1, stopPoints)
canvas.session.updateCanvasProperty("fillStyle", gradient)
}
func (canvas *canvasData) SetRadialGradientStrokeStyle(x0, y0, r0 float64, color0 Color, x1, y1, r1 float64, color1 Color, stopPoints []GradientPoint) {
canvas.setRadialGradient(x0, y0, r0, color0, x1, y1, r1, color1, stopPoints)
canvas.script.WriteString("\nctx.strokeStyle = gradient;")
gradient := canvas.createRadialGradient(x0, y0, r0, color0, x1, y1, r1, color1, stopPoints)
canvas.session.updateCanvasProperty("strokeStyle", gradient)
}
func (canvas *canvasData) SetImageFillStyle(image Image, repeat int) {
@ -528,89 +450,70 @@ func (canvas *canvasData) SetImageFillStyle(image Image, repeat int) {
return
}
canvas.script.WriteString("\nimg = images.get('")
canvas.script.WriteString(image.URL())
canvas.script.WriteString("');\nif (img) {\nctx.fillStyle = ctx.createPattern(img,'")
canvas.script.WriteString(repeatText)
canvas.script.WriteString("');\n}")
canvas.session.callCanvasImageFunc(image.URL(), "fillStyle", "createPattern", repeatText)
}
func (canvas *canvasData) SetLineWidth(width float64) {
if width > 0 {
canvas.script.WriteString("\nctx.lineWidth = '")
canvas.script.WriteString(strconv.FormatFloat(width, 'g', -1, 64))
canvas.script.WriteString("';")
canvas.session.updateCanvasProperty("lineWidth", width)
}
}
func (canvas *canvasData) SetLineJoin(join int) {
switch join {
case MiterJoin:
canvas.script.WriteString("\nctx.lineJoin = 'miter';")
canvas.session.updateCanvasProperty("lineJoin", "miter")
case RoundJoin:
canvas.script.WriteString("\nctx.lineJoin = 'round';")
canvas.session.updateCanvasProperty("lineJoin", "round")
case BevelJoin:
canvas.script.WriteString("\nctx.lineJoin = 'bevel';")
canvas.session.updateCanvasProperty("lineJoin", "bevel")
}
}
func (canvas *canvasData) SetLineCap(cap int) {
switch cap {
case ButtCap:
canvas.script.WriteString("\nctx.lineCap = 'butt';")
canvas.session.updateCanvasProperty("lineCap", "butt")
case RoundCap:
canvas.script.WriteString("\nctx.lineCap = 'round';")
canvas.session.updateCanvasProperty("lineCap", "round")
case SquareCap:
canvas.script.WriteString("\nctx.lineCap = 'square';")
canvas.session.updateCanvasProperty("lineCap", "square")
}
}
func (canvas *canvasData) SetLineDash(dash []float64, offset float64) {
canvas.script.WriteString("\nctx.setLineDash([")
for i, d := range dash {
if i > 0 {
canvas.script.WriteString(",")
}
canvas.script.WriteString(strconv.FormatFloat(d, 'g', -1, 64))
}
canvas.script.WriteString("]);")
canvas.session.callCanvasFunc("setLineDash", dash)
if offset >= 0 {
canvas.script.WriteString("\nctx.lineDashOffset = '")
canvas.script.WriteString(strconv.FormatFloat(offset, 'g', -1, 64))
canvas.script.WriteString("';")
canvas.session.updateCanvasProperty("lineDashOffset", offset)
}
}
func (canvas *canvasData) writeFont(name string, script *strings.Builder) {
names := strings.Split(name, ",")
lead := " "
for _, font := range names {
/*
func (canvas *canvasData) convertFont(name string) string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
for i, font := range strings.Split(name, ",") {
font = strings.Trim(font, " \n\"'")
script.WriteString(lead)
lead = ","
if i > 0 {
buffer.WriteRune(',')
}
if strings.Contains(font, " ") {
script.WriteRune('"')
script.WriteString(font)
script.WriteRune('"')
buffer.WriteRune('"')
buffer.WriteString(font)
buffer.WriteRune('"')
} else {
script.WriteString(font)
buffer.WriteString(font)
}
}
return buffer.String()
}
script.WriteString("';")
}
func (canvas *canvasData) SetFont(name string, size SizeUnit) {
canvas.script.WriteString("\nctx.font = '")
canvas.script.WriteString(size.cssString("1rem", canvas.View().Session()))
canvas.writeFont(name, &canvas.script)
}
*/
func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params FontParams) string {
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
@ -672,135 +575,76 @@ func (canvas *canvasData) fontWithParams(name string, size SizeUnit, params Font
return buffer.String()
}
func (canvas *canvasData) setFontWithParams(name string, size SizeUnit, params FontParams, script *strings.Builder) {
script.WriteString("\nctx.font = '")
if params.Italic {
script.WriteString("italic ")
}
if params.SmallCaps {
script.WriteString("small-caps ")
}
if params.Weight > 0 && params.Weight <= 9 {
switch params.Weight {
case 4:
script.WriteString("normal ")
case 7:
script.WriteString("bold ")
default:
script.WriteString(strconv.Itoa(params.Weight * 100))
script.WriteRune(' ')
}
}
script.WriteString(size.cssString("1rem", canvas.View().Session()))
switch params.LineHeight.Type {
case Auto:
case SizeInPercent:
if params.LineHeight.Value != 100 {
script.WriteString("/")
script.WriteString(strconv.FormatFloat(params.LineHeight.Value/100, 'g', -1, 64))
}
case SizeInFraction:
if params.LineHeight.Value != 1 {
script.WriteString("/")
script.WriteString(strconv.FormatFloat(params.LineHeight.Value, 'g', -1, 64))
}
default:
script.WriteString("/")
script.WriteString(params.LineHeight.cssString("", canvas.View().Session()))
}
canvas.writeFont(name, script)
func (canvas *canvasData) SetFont(name string, size SizeUnit) {
canvas.session.updateCanvasProperty("font", canvas.fontWithParams(name, size, FontParams{}))
}
func (canvas *canvasData) SetFontWithParams(name string, size SizeUnit, params FontParams) {
canvas.setFontWithParams(name, size, params, &canvas.script)
canvas.session.updateCanvasProperty("font", canvas.fontWithParams(name, size, params))
}
func (canvas *canvasData) TextMetrics(text string, fontName string, fontSize SizeUnit, fontParams FontParams) TextMetrics {
view := canvas.View()
return view.Session().canvasTextMetrics(view.htmlID(), canvas.fontWithParams(fontName, fontSize, fontParams), text)
return canvas.session.canvasTextMetrics(canvas.view.htmlID(), canvas.fontWithParams(fontName, fontSize, fontParams), text)
}
func (canvas *canvasData) SetTextBaseline(baseline int) {
switch baseline {
case AlphabeticBaseline:
canvas.script.WriteString("\nctx.textBaseline = 'alphabetic';")
canvas.session.updateCanvasProperty("textBaseline", "alphabetic")
case TopBaseline:
canvas.script.WriteString("\nctx.textBaseline = 'top';")
canvas.session.updateCanvasProperty("textBaseline", "top")
case MiddleBaseline:
canvas.script.WriteString("\nctx.textBaseline = 'middle';")
canvas.session.updateCanvasProperty("textBaseline", "middle")
case BottomBaseline:
canvas.script.WriteString("\nctx.textBaseline = 'bottom';")
canvas.session.updateCanvasProperty("textBaseline", "bottom")
case HangingBaseline:
canvas.script.WriteString("\nctx.textBaseline = 'hanging';")
canvas.session.updateCanvasProperty("textBaseline", "hanging")
case IdeographicBaseline:
canvas.script.WriteString("\nctx.textBaseline = 'ideographic';")
canvas.session.updateCanvasProperty("textBaseline", "ideographic")
}
}
func (canvas *canvasData) SetTextAlign(align int) {
switch align {
case LeftAlign:
canvas.script.WriteString("\nctx.textAlign = 'left';")
canvas.session.updateCanvasProperty("textAlign", "left")
case RightAlign:
canvas.script.WriteString("\nctx.textAlign = 'right';")
canvas.session.updateCanvasProperty("textAlign", "right")
case CenterAlign:
canvas.script.WriteString("\nctx.textAlign = 'center';")
canvas.session.updateCanvasProperty("textAlign", "center")
case StartAlign:
canvas.script.WriteString("\nctx.textAlign = 'start';")
canvas.session.updateCanvasProperty("textAlign", "start")
case EndAlign:
canvas.script.WriteString("\nctx.textAlign = 'end';")
canvas.session.updateCanvasProperty("textAlign", "end")
}
}
func (canvas *canvasData) SetShadow(offsetX, offsetY, blur float64, color Color) {
if color.Alpha() > 0 && blur >= 0 {
canvas.script.WriteString("\nctx.shadowColor = '")
canvas.script.WriteString(color.cssString())
canvas.script.WriteString("';\nctx.shadowOffsetX = ")
canvas.script.WriteString(strconv.FormatFloat(offsetX, 'g', -1, 64))
canvas.script.WriteString(";\nctx.shadowOffsetY = ")
canvas.script.WriteString(strconv.FormatFloat(offsetY, 'g', -1, 64))
canvas.script.WriteString(";\nctx.shadowBlur = ")
canvas.script.WriteString(strconv.FormatFloat(blur, 'g', -1, 64))
canvas.script.WriteString(";")
canvas.session.updateCanvasProperty("shadowColor", color.cssString())
canvas.session.updateCanvasProperty("shadowOffsetX", offsetX)
canvas.session.updateCanvasProperty("shadowOffsetY", offsetY)
canvas.session.updateCanvasProperty("shadowBlur", blur)
}
}
func (canvas *canvasData) ResetShadow() {
canvas.script.WriteString("\nctx.shadowColor = 'rgba(0,0,0,0)';\nctx.shadowOffsetX = 0;\nctx.shadowOffsetY = 0;\nctx.shadowBlur = 0;")
}
func (canvas *canvasData) writeRectArgs(x, y, width, height float64) {
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(width, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(height, 'g', -1, 64))
canvas.session.updateCanvasProperty("shadowColor", "rgba(0,0,0,0)")
canvas.session.updateCanvasProperty("shadowOffsetX", 0)
canvas.session.updateCanvasProperty("shadowOffsetY", 0)
canvas.session.updateCanvasProperty("shadowBlur", 0)
}
func (canvas *canvasData) ClearRect(x, y, width, height float64) {
canvas.script.WriteString("\nctx.clearRect(")
canvas.writeRectArgs(x, y, width, height)
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("clearRect", x, y, width, height)
}
func (canvas *canvasData) FillRect(x, y, width, height float64) {
canvas.script.WriteString("\nctx.fillRect(")
canvas.writeRectArgs(x, y, width, height)
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("fillRect", x, y, width, height)
}
func (canvas *canvasData) StrokeRect(x, y, width, height float64) {
canvas.script.WriteString("\nctx.strokeRect(")
canvas.writeRectArgs(x, y, width, height)
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("strokeRect", x, y, width, height)
}
func (canvas *canvasData) FillAndStrokeRect(x, y, width, height float64) {
@ -808,7 +652,7 @@ func (canvas *canvasData) FillAndStrokeRect(x, y, width, height float64) {
canvas.StrokeRect(x, y, width, height)
}
func (canvas *canvasData) writeRoundedRect(x, y, width, height, r float64) {
func (canvas *canvasData) createRoundedRect(x, y, width, height, r float64) {
left := strconv.FormatFloat(x, 'g', -1, 64)
top := strconv.FormatFloat(y, 'g', -1, 64)
right := strconv.FormatFloat(x+width, 'g', -1, 64)
@ -818,104 +662,65 @@ func (canvas *canvasData) writeRoundedRect(x, y, width, height, r float64) {
rightR := strconv.FormatFloat(x+width-r, 'g', -1, 64)
bottomR := strconv.FormatFloat(y+height-r, 'g', -1, 64)
radius := strconv.FormatFloat(r, 'g', -1, 64)
canvas.script.WriteString("\nctx.beginPath();\nctx.moveTo(")
canvas.script.WriteString(left)
canvas.script.WriteRune(',')
canvas.script.WriteString(topR)
canvas.script.WriteString(");\nctx.arc(")
canvas.script.WriteString(leftR)
canvas.script.WriteRune(',')
canvas.script.WriteString(topR)
canvas.script.WriteRune(',')
canvas.script.WriteString(radius)
canvas.script.WriteString(",Math.PI,Math.PI*3/2);\nctx.lineTo(")
canvas.script.WriteString(rightR)
canvas.script.WriteRune(',')
canvas.script.WriteString(top)
canvas.script.WriteString(");\nctx.arc(")
canvas.script.WriteString(rightR)
canvas.script.WriteRune(',')
canvas.script.WriteString(topR)
canvas.script.WriteRune(',')
canvas.script.WriteString(radius)
canvas.script.WriteString(",Math.PI*3/2,Math.PI*2);\nctx.lineTo(")
canvas.script.WriteString(right)
canvas.script.WriteRune(',')
canvas.script.WriteString(bottomR)
canvas.script.WriteString(");\nctx.arc(")
canvas.script.WriteString(rightR)
canvas.script.WriteRune(',')
canvas.script.WriteString(bottomR)
canvas.script.WriteRune(',')
canvas.script.WriteString(radius)
canvas.script.WriteString(",0,Math.PI/2);\nctx.lineTo(")
canvas.script.WriteString(leftR)
canvas.script.WriteRune(',')
canvas.script.WriteString(bottom)
canvas.script.WriteString(");\nctx.arc(")
canvas.script.WriteString(leftR)
canvas.script.WriteRune(',')
canvas.script.WriteString(bottomR)
canvas.script.WriteRune(',')
canvas.script.WriteString(radius)
canvas.script.WriteString(",Math.PI/2,Math.PI);\nctx.closePath();")
canvas.session.callCanvasFunc("beginPath")
canvas.session.callCanvasFunc("moveTo", left, topR)
canvas.session.callCanvasFunc("arc", leftR, topR, radius, math.Pi, math.Pi*3/2)
canvas.session.callCanvasFunc("lineTo", rightR, top)
canvas.session.callCanvasFunc("arc", rightR, topR, radius, math.Pi*3/2, math.Pi*2)
canvas.session.callCanvasFunc("lineTo", right, bottomR)
canvas.session.callCanvasFunc("arc", rightR, bottomR, radius, 0, math.Pi/2)
canvas.session.callCanvasFunc("lineTo", leftR, bottom)
canvas.session.callCanvasFunc("arc", leftR, bottomR, radius, math.Pi/2, math.Pi)
canvas.session.callCanvasFunc("closePath")
}
func (canvas *canvasData) FillRoundedRect(x, y, width, height, r float64) {
canvas.writeRoundedRect(x, y, width, height, r)
canvas.script.WriteString("\nctx.fill();")
canvas.createRoundedRect(x, y, width, height, r)
canvas.session.callCanvasFunc("fill")
}
func (canvas *canvasData) StrokeRoundedRect(x, y, width, height, r float64) {
canvas.writeRoundedRect(x, y, width, height, r)
canvas.script.WriteString("\nctx.stroke();")
canvas.createRoundedRect(x, y, width, height, r)
canvas.session.callCanvasFunc("stroke")
}
func (canvas *canvasData) FillAndStrokeRoundedRect(x, y, width, height, r float64) {
canvas.writeRoundedRect(x, y, width, height, r)
canvas.script.WriteString("\nctx.fill();\nctx.stroke();")
canvas.createRoundedRect(x, y, width, height, r)
canvas.session.callCanvasFunc("fill")
canvas.session.callCanvasFunc("stroke")
}
func (canvas *canvasData) writeEllipse(x, y, radiusX, radiusY, rotation float64) {
yText := strconv.FormatFloat(y, 'g', -1, 64)
canvas.script.WriteString("\nctx.beginPath();\nctx.moveTo(")
canvas.script.WriteString(strconv.FormatFloat(x+radiusX, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(yText)
canvas.script.WriteString(");\nctx.ellipse(")
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(yText)
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(radiusX, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(radiusY, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(rotation, 'g', -1, 64))
canvas.script.WriteString(",0,Math.PI*2);")
func (canvas *canvasData) createEllipse(x, y, radiusX, radiusY, rotation float64) {
canvas.session.callCanvasFunc("beginPath")
canvas.session.callCanvasFunc("moveTo", x+radiusX, y)
canvas.session.callCanvasFunc("ellipse", x, y, radiusX, radiusY, rotation, 0, math.Pi*2)
//canvas.session.callCanvasFunc("closePath")
}
func (canvas *canvasData) FillEllipse(x, y, radiusX, radiusY, rotation float64) {
if radiusX >= 0 && radiusY >= 0 {
canvas.writeEllipse(x, y, radiusX, radiusY, rotation)
canvas.script.WriteString("\nctx.fill();")
canvas.createEllipse(x, y, radiusX, radiusY, rotation)
canvas.session.callCanvasFunc("fill")
}
}
func (canvas *canvasData) StrokeEllipse(x, y, radiusX, radiusY, rotation float64) {
if radiusX >= 0 && radiusY >= 0 {
canvas.writeEllipse(x, y, radiusX, radiusY, rotation)
canvas.script.WriteString("\nctx.stroke();")
canvas.createEllipse(x, y, radiusX, radiusY, rotation)
canvas.session.callCanvasFunc("stroke")
}
}
func (canvas *canvasData) FillAndStrokeEllipse(x, y, radiusX, radiusY, rotation float64) {
if radiusX >= 0 && radiusY >= 0 {
canvas.writeEllipse(x, y, radiusX, radiusY, rotation)
canvas.script.WriteString("\nctx.fill();\nctx.stroke();")
canvas.createEllipse(x, y, radiusX, radiusY, rotation)
canvas.session.callCanvasFunc("fill")
canvas.session.callCanvasFunc("stroke")
}
}
/*
func (canvas *canvasData) writePointArgs(x, y float64) {
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
@ -947,101 +752,59 @@ func (canvas *canvasData) writeStringArgs(text string, script *strings.Builder)
}
}
}
*/
func (canvas *canvasData) FillText(x, y float64, text string) {
canvas.script.WriteString("\nctx.fillText('")
canvas.writeStringArgs(text, &canvas.script)
canvas.script.WriteString(`',`)
canvas.writePointArgs(x, y)
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("fillText", text, x, y)
}
func (canvas *canvasData) StrokeText(x, y float64, text string) {
canvas.script.WriteString("\nctx.strokeText('")
canvas.writeStringArgs(text, &canvas.script)
canvas.script.WriteString(`',`)
canvas.writePointArgs(x, y)
canvas.script.WriteString(");")
canvas.session.callCanvasFunc("strokeText", text, x, y)
}
func (canvas *canvasData) FillPath(path Path) {
canvas.script.WriteString(path.scriptText())
canvas.script.WriteString("\nctx.fill();")
path.create(canvas.session)
canvas.session.callCanvasFunc("fill")
}
func (canvas *canvasData) StrokePath(path Path) {
canvas.script.WriteString(path.scriptText())
canvas.script.WriteString("\nctx.stroke();")
path.create(canvas.session)
canvas.session.callCanvasFunc("stroke")
}
func (canvas *canvasData) FillAndStrokePath(path Path) {
canvas.script.WriteString(path.scriptText())
canvas.script.WriteString("\nctx.fill();\nctx.stroke();")
path.create(canvas.session)
canvas.session.callCanvasFunc("fill")
canvas.session.callCanvasFunc("stroke")
}
func (canvas *canvasData) DrawLine(x0, y0, x1, y1 float64) {
canvas.script.WriteString("\nctx.beginPath();\nctx.moveTo(")
canvas.script.WriteString(strconv.FormatFloat(x0, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y0, 'g', -1, 64))
canvas.script.WriteString(");\nctx.lineTo(")
canvas.script.WriteString(strconv.FormatFloat(x1, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y1, 'g', -1, 64))
canvas.script.WriteString(");\nctx.stroke();")
canvas.session.callCanvasFunc("beginPath")
canvas.session.callCanvasFunc("moveTo", x0, y0)
canvas.session.callCanvasFunc("lineTo", x1, y1)
canvas.session.callCanvasFunc("stroke")
}
func (canvas *canvasData) DrawImage(x, y float64, image Image) {
if image == nil || image.LoadingStatus() != ImageReady {
return
}
canvas.script.WriteString("\nimg = images.get('")
canvas.script.WriteString(image.URL())
canvas.script.WriteString("');\nif (img) {\nctx.drawImage(img,")
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
canvas.script.WriteString(");\n}")
canvas.session.callCanvasImageFunc(image.URL(), "", "drawImage", x, y)
}
func (canvas *canvasData) DrawImageInRect(x, y, width, height float64, image Image) {
if image == nil || image.LoadingStatus() != ImageReady {
return
}
canvas.script.WriteString("\nimg = images.get('")
canvas.script.WriteString(image.URL())
canvas.script.WriteString("');\nif (img) {\nctx.drawImage(img,")
canvas.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(width, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(height, 'g', -1, 64))
canvas.script.WriteString(");\n}")
canvas.session.callCanvasImageFunc(image.URL(), "", "drawImage", x, y, width, height)
}
func (canvas *canvasData) DrawImageFragment(srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight float64, image Image) {
if image == nil || image.LoadingStatus() != ImageReady {
return
}
canvas.script.WriteString("\nimg = images.get('")
canvas.script.WriteString(image.URL())
canvas.script.WriteString("');\nif (img) {\nctx.drawImage(img,")
canvas.script.WriteString(strconv.FormatFloat(srcX, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(srcY, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(srcWidth, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(srcHeight, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(dstX, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(dstY, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(dstWidth, 'g', -1, 64))
canvas.script.WriteRune(',')
canvas.script.WriteString(strconv.FormatFloat(dstHeight, 'g', -1, 64))
canvas.script.WriteString(");\n}")
canvas.session.callCanvasImageFunc(image.URL(), "", "drawImage", srcX, srcY, srcWidth, srcHeight, dstX, dstY, dstWidth, dstHeight)
}

View File

@ -107,7 +107,7 @@ func (canvasView *canvasViewData) Redraw() {
if canvasView.drawer != nil {
canvasView.drawer(canvas)
}
canvasView.session.runScript(canvas.finishDraw())
canvas.finishDraw()
}
}

View File

@ -1,7 +1,10 @@
package rui
import (
"encoding/base64"
"fmt"
"path/filepath"
"runtime"
"strings"
)
@ -129,12 +132,10 @@ func (imageView *imageViewData) set(tag string, value any) bool {
if text, ok := value.(string); ok {
imageView.properties[Source] = text
if imageView.created {
src := text
if src != "" && src[0] == '@' {
src, _ = imageProperty(imageView, Source, imageView.session)
}
src, srcset := imageView.src(text)
imageView.session.updateProperty(imageView.htmlID(), "src", src)
if srcset := imageView.srcSet(src); srcset != "" {
if srcset != "" {
imageView.session.updateProperty(imageView.htmlID(), "srcset", srcset)
} else {
imageView.session.removeProperty(imageView.htmlID(), "srcset")
@ -214,29 +215,51 @@ func (imageView *imageViewData) htmlTag() string {
return "img"
}
/*
func (imageView *imageViewData) closeHTMLTag() bool {
return false
func (imageView *imageViewData) src(src string) (string, string) {
if src != "" && src[0] == '@' {
if image, ok := imageView.Session().ImageConstant(src[1:]); ok {
src = image
} else {
src = ""
}
}
if src != "" {
srcset := imageView.srcSet(src)
if runtime.GOOS == "js" {
if image, ok := resources.images[src]; ok && image.fs != nil {
dataType := map[string]string{
".svg": "data:image/svg+xml",
".png": "data:image/png",
".jpg": "data:image/jpg",
".jpeg": "data:image/jpg",
".gif": "data:image/gif",
}
ext := strings.ToLower(filepath.Ext(src))
if prefix, ok := dataType[ext]; ok {
if data, err := image.fs.ReadFile(image.path); err == nil {
return prefix + ";base64," + base64.StdEncoding.EncodeToString(data), ""
} else {
DebugLog(err.Error())
}
}
}
}
return src, srcset
}
return "", ""
}
*/
func (imageView *imageViewData) htmlProperties(self View, buffer *strings.Builder) {
imageView.viewData.htmlProperties(self, buffer)
if imageResource, ok := imageProperty(imageView, Source, imageView.Session()); ok && imageResource != "" {
if imageResource[0] == '@' {
if image, ok := imageView.Session().ImageConstant(imageResource[1:]); ok {
imageResource = image
} else {
imageResource = ""
}
}
if imageResource != "" {
if src, srcset := imageView.src(imageResource); src != "" {
buffer.WriteString(` src="`)
buffer.WriteString(imageResource)
buffer.WriteString(src)
buffer.WriteString(`"`)
if srcset := imageView.srcSet(imageResource); srcset != "" {
if srcset != "" {
buffer.WriteString(` srcset="`)
buffer.WriteString(srcset)
buffer.WriteString(`"`)

110
path.go
View File

@ -1,10 +1,5 @@
package rui
import (
"strconv"
"strings"
)
// Path is a path interface
type Path interface {
// Reset erases the Path
@ -63,134 +58,79 @@ type Path interface {
// If the shape has already been closed or has only one point, this function does nothing.
Close()
scriptText() string
create(session Session)
}
type pathElement struct {
funcName string
args []any
}
type pathData struct {
script strings.Builder
elements []pathElement
}
// NewPath creates a new empty Path
func NewPath() Path {
path := new(pathData)
path.script.Grow(4096)
path.script.WriteString("\nctx.beginPath();")
path.Reset()
return path
}
func (path *pathData) Reset() {
path.script.Reset()
path.script.WriteString("\nctx.beginPath();")
path.elements = []pathElement{
pathElement{funcName: "beginPath", args: []any{}},
}
}
func (path *pathData) MoveTo(x, y float64) {
path.script.WriteString("\nctx.moveTo(")
path.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
path.script.WriteString(");")
path.elements = append(path.elements, pathElement{funcName: "moveTo", args: []any{x, y}})
}
func (path *pathData) LineTo(x, y float64) {
path.script.WriteString("\nctx.lineTo(")
path.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
path.script.WriteString(");")
path.elements = append(path.elements, pathElement{funcName: "lineTo", args: []any{x, y}})
}
func (path *pathData) ArcTo(x0, y0, x1, y1, radius float64) {
if radius > 0 {
path.script.WriteString("\nctx.arcTo(")
path.script.WriteString(strconv.FormatFloat(x0, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(y0, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(x1, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(y1, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(radius, 'g', -1, 64))
path.script.WriteString(");")
path.elements = append(path.elements, pathElement{funcName: "arcTo", args: []any{x0, y0, x1, y1, radius}})
}
}
func (path *pathData) Arc(x, y, radius, startAngle, endAngle float64, clockwise bool) {
if radius > 0 {
path.script.WriteString("\nctx.arc(")
path.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(radius, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(startAngle, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(endAngle, 'g', -1, 64))
if !clockwise {
path.script.WriteString(",true);")
path.elements = append(path.elements, pathElement{funcName: "arc", args: []any{x, y, radius, startAngle, endAngle, true}})
} else {
path.script.WriteString(");")
path.elements = append(path.elements, pathElement{funcName: "arc", args: []any{x, y, radius, startAngle, endAngle}})
}
}
}
func (path *pathData) BezierCurveTo(cp0x, cp0y, cp1x, cp1y, x, y float64) {
path.script.WriteString("\nctx.bezierCurveTo(")
path.script.WriteString(strconv.FormatFloat(cp0x, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(cp0y, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(cp1x, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(cp1y, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
path.script.WriteString(");")
path.elements = append(path.elements, pathElement{funcName: "bezierCurveTo", args: []any{cp0x, cp0y, cp1x, cp1y, x, y}})
}
func (path *pathData) QuadraticCurveTo(cpx, cpy, x, y float64) {
path.script.WriteString("\nctx.quadraticCurveTo(")
path.script.WriteString(strconv.FormatFloat(cpx, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(cpy, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
path.script.WriteString(");")
path.elements = append(path.elements, pathElement{funcName: "quadraticCurveTo", args: []any{cpx, cpy, x, y}})
}
func (path *pathData) Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, clockwise bool) {
if radiusX > 0 && radiusY > 0 {
path.script.WriteString("\nctx.ellipse(")
path.script.WriteString(strconv.FormatFloat(x, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(y, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(radiusX, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(radiusY, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(rotation, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(startAngle, 'g', -1, 64))
path.script.WriteRune(',')
path.script.WriteString(strconv.FormatFloat(endAngle, 'g', -1, 64))
if !clockwise {
path.script.WriteString(",true);")
path.elements = append(path.elements, pathElement{funcName: "ellipse", args: []any{x, y, radiusX, radiusY, rotation, startAngle, endAngle, true}})
} else {
path.script.WriteString(");")
path.elements = append(path.elements, pathElement{funcName: "ellipse", args: []any{x, y, radiusX, radiusY, rotation, startAngle, endAngle}})
}
}
}
func (path *pathData) Close() {
path.script.WriteString("\nctx.close();")
path.elements = append(path.elements, pathElement{funcName: "close", args: []any{}})
}
func (path *pathData) scriptText() string {
return path.script.String()
func (path *pathData) create(session Session) {
for _, element := range path.elements {
session.callCanvasFunc(element.funcName, element.args...)
}
}

View File

@ -123,7 +123,7 @@ func scanEmbedImagesDir(fs *embed.FS, dir, prefix string) {
} else {
ext := strings.ToLower(filepath.Ext(name))
switch ext {
case ".png", ".jpg", ".jpeg", ".svg":
case ".png", ".jpg", ".jpeg", ".svg", ".gif", ".bmp":
registerImage(fs, path, prefix+name)
}
}

View File

@ -18,6 +18,13 @@ type webBrige interface {
removeProperty(htmlID, property string)
readMessage() (string, bool)
writeMessage(text string) bool
cavnasStart(htmlID string)
callCanvasFunc(funcName string, args ...any)
callCanvasVarFunc(v any, funcName string, args ...any)
callCanvasImageFunc(url string, property string, funcName string, args ...any)
createCanvasVar(funcName string, args ...any) any
updateCanvasProperty(property string, value any)
cavnasFinish()
canvasTextMetrics(htmlID, font, text string) TextMetrics
htmlPropertyValue(htmlID, name string) string
answerReceived(answer DataObject)
@ -115,6 +122,13 @@ type Session interface {
runScript(script string)
startUpdateScript(htmlID string) bool
finishUpdateScript(htmlID string)
cavnasStart(htmlID string)
callCanvasFunc(funcName string, args ...any)
createCanvasVar(funcName string, args ...any) any
callCanvasVarFunc(v any, funcName string, args ...any)
callCanvasImageFunc(url string, property string, funcName string, args ...any)
updateCanvasProperty(property string, value any)
cavnasFinish()
canvasTextMetrics(htmlID, font, text string) TextMetrics
htmlPropertyValue(htmlID, name string) string
handleAnswer(data DataObject)
@ -395,6 +409,49 @@ func (session *sessionData) finishUpdateScript(htmlID string) {
}
}
func (session *sessionData) cavnasStart(htmlID string) {
if session.brige != nil {
session.brige.cavnasStart(htmlID)
}
}
func (session *sessionData) callCanvasFunc(funcName string, args ...any) {
if session.brige != nil {
session.brige.callCanvasFunc(funcName, args...)
}
}
func (session *sessionData) updateCanvasProperty(property string, value any) {
if session.brige != nil {
session.brige.updateCanvasProperty(property, value)
}
}
func (session *sessionData) createCanvasVar(funcName string, args ...any) any {
if session.brige != nil {
return session.brige.createCanvasVar(funcName, args...)
}
return nil
}
func (session *sessionData) callCanvasVarFunc(v any, funcName string, args ...any) {
if session.brige != nil && v != nil {
session.brige.callCanvasVarFunc(v, funcName, args...)
}
}
func (session *sessionData) callCanvasImageFunc(url string, property string, funcName string, args ...any) {
if session.brige != nil {
session.brige.callCanvasImageFunc(url, property, funcName, args...)
}
}
func (session *sessionData) cavnasFinish() {
if session.brige != nil {
session.brige.cavnasFinish()
}
}
func (session *sessionData) runScript(script string) {
if session.brige != nil {
session.brige.writeMessage(script)

View File

@ -19,9 +19,15 @@ type wsBrige struct {
answerMutex sync.Mutex
closed bool
buffer strings.Builder
canvasBuffer strings.Builder
canvasVarNumber int
updateScripts map[string]*strings.Builder
}
type canvasVar struct {
name string
}
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 8096,
@ -119,6 +125,21 @@ func (brige *wsBrige) argToString(arg any) (string, bool) {
case float64:
return fmt.Sprintf("%g", arg), true
case []float64:
buffer := allocStringBuilder()
defer freeStringBuilder(buffer)
lead := '['
for _, val := range arg {
buffer.WriteRune(lead)
lead = ','
buffer.WriteString(fmt.Sprintf("%g", val))
}
buffer.WriteRune(']')
return buffer.String(), true
case canvasVar:
return arg.name, true
default:
if n, ok := isInt(arg); ok {
return fmt.Sprintf("%d", n), true
@ -203,6 +224,110 @@ func (brige *wsBrige) removeProperty(htmlID, property string) {
}
}
func (brige *wsBrige) cavnasStart(htmlID string) {
brige.canvasBuffer.Reset()
brige.canvasBuffer.WriteString(`const ctx = getCanvasContext('`)
brige.canvasBuffer.WriteString(htmlID)
brige.canvasBuffer.WriteString(`');`)
}
func (brige *wsBrige) callCanvasFunc(funcName string, args ...any) {
brige.canvasBuffer.WriteString("\nctx.")
brige.canvasBuffer.WriteString(funcName)
brige.canvasBuffer.WriteRune('(')
for i, arg := range args {
if i > 0 {
brige.canvasBuffer.WriteString(", ")
}
argText, _ := brige.argToString(arg)
brige.canvasBuffer.WriteString(argText)
}
brige.canvasBuffer.WriteString(");")
}
func (brige *wsBrige) updateCanvasProperty(property string, value any) {
brige.canvasBuffer.WriteString("\nctx.")
brige.canvasBuffer.WriteString(property)
brige.canvasBuffer.WriteString(" = ")
argText, _ := brige.argToString(value)
brige.canvasBuffer.WriteString(argText)
brige.canvasBuffer.WriteString(";")
}
func (brige *wsBrige) createCanvasVar(funcName string, args ...any) any {
brige.canvasVarNumber++
result := canvasVar{name: fmt.Sprintf("v%d", brige.canvasVarNumber)}
brige.canvasBuffer.WriteString("\nvar ")
brige.canvasBuffer.WriteString(result.name)
brige.canvasBuffer.WriteString(" = ctx.")
brige.canvasBuffer.WriteString(funcName)
brige.canvasBuffer.WriteRune('(')
for i, arg := range args {
if i > 0 {
brige.canvasBuffer.WriteString(", ")
}
argText, _ := brige.argToString(arg)
brige.canvasBuffer.WriteString(argText)
}
brige.canvasBuffer.WriteString(");")
return result
}
func (brige *wsBrige) callCanvasVarFunc(v any, funcName string, args ...any) {
varName, ok := v.(canvasVar)
if !ok {
return
}
brige.canvasBuffer.WriteString("\n")
brige.canvasBuffer.WriteString(varName.name)
brige.canvasBuffer.WriteRune('.')
brige.canvasBuffer.WriteString(funcName)
brige.canvasBuffer.WriteRune('(')
for i, arg := range args {
if i > 0 {
brige.canvasBuffer.WriteString(", ")
}
argText, _ := brige.argToString(arg)
brige.canvasBuffer.WriteString(argText)
}
brige.canvasBuffer.WriteString(");")
}
func (brige *wsBrige) callCanvasImageFunc(url string, property string, funcName string, args ...any) {
brige.canvasBuffer.WriteString("\nimg = images.get('")
brige.canvasBuffer.WriteString(url)
brige.canvasBuffer.WriteString("');\nif (img) {\n")
if property != "" {
brige.canvasBuffer.WriteString("ctx.")
brige.canvasBuffer.WriteString(property)
brige.canvasBuffer.WriteString(" = ")
}
brige.canvasBuffer.WriteString("ctx.")
brige.canvasBuffer.WriteString(funcName)
brige.canvasBuffer.WriteString("(img")
for _, arg := range args {
brige.canvasBuffer.WriteString(", ")
argText, _ := brige.argToString(arg)
brige.canvasBuffer.WriteString(argText)
}
brige.canvasBuffer.WriteString(");\n}")
}
func (brige *wsBrige) cavnasFinish() {
brige.canvasBuffer.WriteString("\n")
script := brige.canvasBuffer.String()
if ProtocolInDebugLog {
DebugLog("Run script:")
DebugLog(script)
}
if brige.conn == nil {
ErrorLog("No connection")
} else if err := brige.conn.WriteMessage(websocket.TextMessage, []byte(script)); err != nil {
ErrorLog(err.Error())
}
}
func (brige *wsBrige) readMessage() (string, bool) {
_, p, err := brige.conn.ReadMessage()
if err != nil {