diff --git a/CHANGELOG.md b/CHANGELOG.md index e595839..88754b2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ * Added "transform" property and Transform interface * Added OpenRawResource function * Added RemoveClientItem method to Session interface +* Added NewPath and NewPathFromSvg methods to Canvas interface +* Removed NewPath function +* Removed Reset methods from Path interface # v0.16.0 * Can use ListAdapter as "content" property value of ListLayout diff --git a/README-ru.md b/README-ru.md index 11a5653..bd001cc 100644 --- a/README-ru.md +++ b/README-ru.md @@ -4443,7 +4443,17 @@ rotation - угол поворота эллипса относительно ц #### Path -Интерфейс Path позволяет описать сложную фигуру. Создается Path с помощью функции NewPath(). +Интерфейс Path позволяет описать сложную фигуру. Для создания объекта Path используются два метода Canvas: + + NewPath() Path + NewPathFromSvg(data string) Path + +Метод NewPath() создает пустую фигуру. Далее вы должны описать фигуру используя методы интерфейса Path + +Метод NewPathFromSvg(data string) Path создает фигуру описанную в параметре data. +Параметр data является описанием фигуры в формате елемента svg изображения. Например + + path := canvas.NewPathFromSvg("m 305,77.362183 c 0,0 -3,8.648649 -13,21.621616 -10,12.972981 -17,18.378381 -17,18.378381 0,0 6,4.32433 17,18.37838 11,14.05406 13,21.62162 13,21.62162 0,0 7.83867,-14.92584 13,-21.62162 10,-12.97297 17,-18.37838 17,-18.37838 0,0 -7,-5.4054 -17,-18.378381 C 308,86.010832 305,77.362183 305,77.362183 Z") После создания вы должны описать фигуру. Для этого могут использоваться следующие функции интерфейса: diff --git a/README.md b/README.md index abab312..8cc1574 100644 --- a/README.md +++ b/README.md @@ -4411,7 +4411,17 @@ rotation - the angle of rotation of the ellipse relative to the center in radian #### Path -The Path interface allows you to describe a complex shape. Path is created using the NewPath () function. +The Path interface allows you to describe a complex shape. Two Canvas methods are used to create a Path object: + + NewPath() Path + NewPathFromSvg(data string) Path + +The NewPath() method creates an empty shape. Next, you must describe the shape using the methods of the Path interface + +The NewPathFromSvg(data string) Path method creates the shape described in the data parameter. +The data parameter is a description of the shape in the format of a svg image element. For example + + path := canvas.NewPathFromSvg("m 305,77.362183 c 0,0 -3,8.648649 -13,21.621616 -10,12.972981 -17,18.378381 -17,18.378381 0,0 6,4.32433 17,18.37838 11,14.05406 13,21.62162 13,21.62162 0,0 7.83867,-14.92584 13,-21.62162 10,-12.97297 17,-18.37838 17,-18.37838 0,0 -7,-5.4054 -17,-18.378381 C 308,86.010832 305,77.362183 305,77.362183 Z") Once created, you must describe the shape. For this, the following interface functions can be used: diff --git a/app_scripts.js b/app_scripts.js index afa0445..5b91d49 100644 --- a/app_scripts.js +++ b/app_scripts.js @@ -2136,3 +2136,11 @@ function setCssVar(tag, value) { root.style.setProperty(tag, value); } } + +function createPath2D(svg) { + if (svg) { + return new Path2D(svg); + } else { + return new Path2D(); + } +} \ No newline at end of file diff --git a/canvas.go b/canvas.go index 0f532cd..76a92bc 100644 --- a/canvas.go +++ b/canvas.go @@ -263,6 +263,11 @@ type Canvas interface { // and is stroked (outlined) according to the current strokeStyle and other context settings FillAndStrokeEllipse(x, y, radiusX, radiusY, rotation float64) + // NewPath creates a new Path object + NewPath() Path + // NewPathFromSvg creates a new Path and initialize it by a string consisting of SVG path data + NewPathFromSvg(data string) Path + // FillPath draws a path that is filled according to the current FillStyle. FillPath(path Path) // StrokePath draws a path that is stroked (outlined) according to the current strokeStyle @@ -346,8 +351,7 @@ func (canvas *canvasData) ClipRect(x, y, width, height float64) { } func (canvas *canvasData) ClipPath(path Path) { - path.create(canvas.session) - canvas.session.callCanvasFunc("clip") + canvas.session.callCanvasFunc("clip", path.obj()) } func (canvas *canvasData) SetScale(x, y float64) { @@ -779,19 +783,16 @@ func (canvas *canvasData) StrokeText(x, y float64, text string) { } func (canvas *canvasData) FillPath(path Path) { - path.create(canvas.session) - canvas.session.callCanvasFunc("fill") + canvas.session.callCanvasFunc("fill", path.obj()) } func (canvas *canvasData) StrokePath(path Path) { - path.create(canvas.session) - canvas.session.callCanvasFunc("stroke") + canvas.session.callCanvasFunc("stroke", path.obj()) } func (canvas *canvasData) FillAndStrokePath(path Path) { - path.create(canvas.session) - canvas.session.callCanvasFunc("fill") - canvas.session.callCanvasFunc("stroke") + canvas.session.callCanvasFunc("fill", path.obj()) + canvas.session.callCanvasFunc("stroke", path.obj()) } func (canvas *canvasData) DrawLine(x0, y0, x1, y1 float64) { diff --git a/data.go b/data.go index c0fd968..038b3b3 100644 --- a/data.go +++ b/data.go @@ -134,7 +134,7 @@ func (object *dataObject) PropertyObject(tag string) DataObject { } func (object *dataObject) setNode(node DataNode) { - if object.property == nil || len(object.property) == 0 { + if len(object.property) == 0 { object.property = []DataNode{node} } else { tag := node.Tag() diff --git a/httpHandler.go b/httpHandler.go index 0593397..b0757fd 100644 --- a/httpHandler.go +++ b/httpHandler.go @@ -1,3 +1,5 @@ +//go:build !wasm + package rui import ( @@ -39,6 +41,7 @@ Example for echo: } }) */ + func NewHandler(urlPrefix string, createContentFunc func(Session) SessionContent, params AppParams) *httpHandler { app := new(application) app.params = params diff --git a/path.go b/path.go index 5425b33..aba8fe0 100644 --- a/path.go +++ b/path.go @@ -2,9 +2,6 @@ package rui // Path is a path interface type Path interface { - // Reset erases the Path - Reset() - // MoveTo begins a new sub-path at the point specified by the given (x, y) coordinates MoveTo(x, y float64) @@ -58,79 +55,76 @@ type Path interface { // If the shape has already been closed or has only one point, this function does nothing. Close() - create(session Session) -} - -type pathElement struct { - funcName string - args []any + //create(session Session) + obj() any } type pathData struct { - elements []pathElement + session Session + varName any } // NewPath creates a new empty Path -func NewPath() Path { +func (canvas *canvasData) NewPath() Path { path := new(pathData) - path.Reset() + path.session = canvas.session + path.varName = canvas.session.createPath("") return path } -func (path *pathData) Reset() { - path.elements = []pathElement{ - {funcName: "beginPath", args: []any{}}, - } +func (canvas *canvasData) NewPathFromSvg(data string) Path { + path := new(pathData) + path.session = canvas.session + path.varName = canvas.session.createPath(data) + return path } func (path *pathData) MoveTo(x, y float64) { - path.elements = append(path.elements, pathElement{funcName: "moveTo", args: []any{x, y}}) + path.session.callCanvasVarFunc(path.varName, "moveTo", x, y) } func (path *pathData) LineTo(x, y float64) { - path.elements = append(path.elements, pathElement{funcName: "lineTo", args: []any{x, y}}) + path.session.callCanvasVarFunc(path.varName, "lineTo", x, y) } func (path *pathData) ArcTo(x0, y0, x1, y1, radius float64) { if radius > 0 { - path.elements = append(path.elements, pathElement{funcName: "arcTo", args: []any{x0, y0, x1, y1, radius}}) + path.session.callCanvasVarFunc(path.varName, "arcTo", x0, y0, x1, y1, radius) } } func (path *pathData) Arc(x, y, radius, startAngle, endAngle float64, clockwise bool) { if radius > 0 { if !clockwise { - path.elements = append(path.elements, pathElement{funcName: "arc", args: []any{x, y, radius, startAngle, endAngle, true}}) + path.session.callCanvasVarFunc(path.varName, "arc", x, y, radius, startAngle, endAngle, true) } else { - path.elements = append(path.elements, pathElement{funcName: "arc", args: []any{x, y, radius, startAngle, endAngle}}) + path.session.callCanvasVarFunc(path.varName, "arc", x, y, radius, startAngle, endAngle) } } } func (path *pathData) BezierCurveTo(cp0x, cp0y, cp1x, cp1y, x, y float64) { - path.elements = append(path.elements, pathElement{funcName: "bezierCurveTo", args: []any{cp0x, cp0y, cp1x, cp1y, x, y}}) + path.session.callCanvasVarFunc(path.varName, "bezierCurveTo", cp0x, cp0y, cp1x, cp1y, x, y) } func (path *pathData) QuadraticCurveTo(cpx, cpy, x, y float64) { - path.elements = append(path.elements, pathElement{funcName: "quadraticCurveTo", args: []any{cpx, cpy, x, y}}) + path.session.callCanvasVarFunc(path.varName, "quadraticCurveTo", cpx, cpy, x, y) } func (path *pathData) Ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle float64, clockwise bool) { if radiusX > 0 && radiusY > 0 { if !clockwise { - path.elements = append(path.elements, pathElement{funcName: "ellipse", args: []any{x, y, radiusX, radiusY, rotation, startAngle, endAngle, true}}) + path.session.callCanvasVarFunc(path.varName, "ellipse", x, y, radiusX, radiusY, rotation, startAngle, endAngle, true) } else { - path.elements = append(path.elements, pathElement{funcName: "ellipse", args: []any{x, y, radiusX, radiusY, rotation, startAngle, endAngle}}) + path.session.callCanvasVarFunc(path.varName, "ellipse", x, y, radiusX, radiusY, rotation, startAngle, endAngle) } } } func (path *pathData) Close() { - path.elements = append(path.elements, pathElement{funcName: "close", args: []any{}}) + path.session.callCanvasVarFunc(path.varName, "close") } -func (path *pathData) create(session Session) { - for _, element := range path.elements { - session.callCanvasFunc(element.funcName, element.args...) - } +func (path *pathData) obj() any { + return path.varName } diff --git a/popup.go b/popup.go index f60da10..281ead0 100644 --- a/popup.go +++ b/popup.go @@ -683,7 +683,7 @@ func (manager *popupManager) showPopup(popup Popup) { } session := popup.Session() - if manager.popups == nil || len(manager.popups) == 0 { + if len(manager.popups) == 0 { manager.popups = []Popup{popup} } else { manager.popups = append(manager.popups, popup) diff --git a/session.go b/session.go index 5ebe916..23025f9 100644 --- a/session.go +++ b/session.go @@ -24,6 +24,7 @@ type bridge interface { callCanvasVarFunc(v any, funcName string, args ...any) callCanvasImageFunc(url string, property string, funcName string, args ...any) createCanvasVar(funcName string, args ...any) any + createPath2D(arg string) any updateCanvasProperty(property string, value any) canvasFinish() canvasTextMetrics(htmlID, font, text string) TextMetrics @@ -151,6 +152,7 @@ type Session interface { canvasStart(htmlID string) bool callCanvasFunc(funcName string, args ...any) createCanvasVar(funcName string, args ...any) any + createPath(arg string) any callCanvasVarFunc(v any, funcName string, args ...any) callCanvasImageFunc(url string, property string, funcName string, args ...any) updateCanvasProperty(property string, value any) @@ -561,6 +563,13 @@ func (session *sessionData) createCanvasVar(funcName string, args ...any) any { return nil } +func (session *sessionData) createPath(arg string) any { + if session.bridge != nil { + return session.bridge.createPath2D(arg) + } + return nil +} + func (session *sessionData) callCanvasVarFunc(v any, funcName string, args ...any) { if session.bridge != nil && v != nil { session.bridge.callCanvasVarFunc(v, funcName, args...) diff --git a/sessionTheme.go b/sessionTheme.go index 2706ce0..a9fdbf2 100644 --- a/sessionTheme.go +++ b/sessionTheme.go @@ -313,7 +313,7 @@ func (session *sessionData) Language() string { return session.language } - if session.languages != nil && len(session.languages) > 0 { + if len(session.languages) > 0 { return session.languages[0] } diff --git a/viewClip.go b/viewClip.go index d8074ea..574f01e 100644 --- a/viewClip.go +++ b/viewClip.go @@ -454,11 +454,7 @@ func (clip *polygonClip) cssStyle(session Session) string { } func (clip *polygonClip) valid(session Session) bool { - if clip.points == nil || len(clip.points) == 0 { - return false - } - - return true + return len(clip.points) > 0 } func parseClipShape(obj DataObject) ClipShape { diff --git a/viewsContainer.go b/viewsContainer.go index bbae50b..7ef7163 100644 --- a/viewsContainer.go +++ b/viewsContainer.go @@ -66,7 +66,7 @@ func (container *viewsContainerData) Append(view View) { if view != nil { htmlID := container.htmlID() view.setParentID(htmlID) - if container.views == nil || len(container.views) == 0 { + if len(container.views) == 0 { container.views = []View{view} } else { container.views = append(container.views, view) diff --git a/wasmBridge.go b/wasmBridge.go index bc1902e..347e685 100644 --- a/wasmBridge.go +++ b/wasmBridge.go @@ -193,6 +193,17 @@ func (bridge *wasmBridge) createCanvasVar(funcName string, args ...any) any { return result } +func (bridge *wasmBridge) createPath2D(arg string) any { + if arg != "" { + result := bridge.canvas.Call("createPath2D", arg) + bridge.printFuncToLog("var "+result.String()+" = new Path2D", arg) + return result + } + result := bridge.canvas.Call("createPath2D") + bridge.printFuncToLog("var " + result.String() + " = new Path2D") + return result +} + func (bridge *wasmBridge) updateCanvasProperty(property string, value any) { if !bridge.canvas.IsNull() { if ProtocolInDebugLog { diff --git a/webBridge.go b/webBridge.go index 4192951..5901585 100644 --- a/webBridge.go +++ b/webBridge.go @@ -350,6 +350,20 @@ func (bridge *webBridge) createCanvasVar(funcName string, args ...any) any { return result } +func (bridge *webBridge) createPath2D(arg string) any { + bridge.canvasVarNumber++ + result := canvasVar{name: fmt.Sprintf("v%d", bridge.canvasVarNumber)} + bridge.canvasBuffer.WriteString("\nlet ") + bridge.canvasBuffer.WriteString(result.name) + bridge.canvasBuffer.WriteString(` = new Path2D(`) + if arg != "" { + argText, _ := bridge.argToString(arg) + bridge.canvasBuffer.WriteString(argText) + } + bridge.canvasBuffer.WriteString(`);`) + return result +} + func (bridge *webBridge) callCanvasVarFunc(v any, funcName string, args ...any) { varName, ok := v.(canvasVar) if !ok {