Giter Club home page Giter Club logo

canvas's Introduction

canvas

PkgGoDev Build Status Go Report Card

This Go module utilizes WebSockets to establish communication with a 2D canvas graphics context in a web browser, providing a portable way to create interactive 2D graphics from within a Go program.

The Go program (server) sends draw commands to the web browser (client) via WebSocket using a binary format. In return, the client sends keyboard, mouse, and touch events to the server.

This module does not rely on operating system-specific backends or Cgo bindings. It also does not utilize WebAssembly, which means the Go code runs on the server side, rather than in the browser. The client-server design enables the canvas to be displayed on a different machine over the network.

Examples

The example subdirectory contains a variety of demo programs.

Screenshots of examples

Usage

Drawing

The ListenAndServe function initializes the canvas server and takes the following arguments: the network address with the port number to bind to, a run function, and an options structure that configures various aspects such as the canvas size in pixels or a title for the browser tab.

The run function is called when a client connects to the server. This serves as the entry point for drawing.

package main

import (
	"image/color"
	"log"

	"github.com/fzipp/canvas"
)

func main() {
	err := canvas.ListenAndServe(":8080", run, &canvas.Options{
		Title:  "Example 1: Drawing",
		Width:  100,
		Height: 80,
	})
	if err != nil {
		log.Fatal(err)
	}
}

func run(ctx *canvas.Context) {
	ctx.SetFillStyle(color.RGBA{R: 200, A: 255})
	ctx.FillRect(10, 10, 50, 50)
	// ...
	ctx.Flush()
}

After starting the program, you can access the canvas by opening http://localhost:8080 in a web browser.

The server doesn't immediately send each drawing operation to the client but instead buffers them until the Flush method is called. The flush should occur once the image or an animation frame is complete; otherwise, nothing will be displayed.

Each client connection starts its own run function as a goroutine. Access to shared state between client connections must be synchronized. If you don't want to share state between connections, you should keep it local to the run function and pass the state to other functions called by the run function.

An animation loop

To create an animation, you can use a for loop within the run function. Inside this loop, observe the ctx.Events() channel for a canvas.CloseEvent to exit the loop when the connection is closed.

A useful pattern is to create a struct that holds the animation state and has both an update and a draw method:

package main

import (
	"log"
	"time"

	"github.com/fzipp/canvas"
)

func main() {
	err := canvas.ListenAndServe(":8080", run, &canvas.Options{
		Title:  "Example 2: Animation",
		Width:  800,
		Height: 600,
	})
	if err != nil {
		log.Fatal(err)
	}
}

func run(ctx *canvas.Context) {
	d := &demo{}
	for {
		select {
		case event := <-ctx.Events():
			if _, ok := event.(canvas.CloseEvent); ok {
				return
			}
		default:
			d.update()
			d.draw(ctx)
			ctx.Flush()
			time.Sleep(time.Second / 6)
		}
	}
}

type demo struct {
	// Animation state, for example:
	x, y int
	// ...
}

func (d *demo) update() {
	// Update animation state for the next frame
	// ...
}

func (d *demo) draw(ctx *canvas.Context) {
	// Draw the frame here, based on the animation state
	// ...
}

Keyboard, mouse and touch events

To handle keyboard, mouse, and touch events, you need to specify which events the client should observe and send to the server. This is achieved by passing an EnabledEvents option to the ListenAndServe function. Mouse move events typically generate more WebSocket communication than the others, so you may want to enable them only if necessary.

The ctx.Events() channel receives the observed events, and a type switch is used to determine the specific event type. A useful pattern involves creating a handle method for event handling:

package main

import (
	"log"

	"github.com/fzipp/canvas"
)

func main() {
	err := canvas.ListenAndServe(":8080", run, &canvas.Options{
		Title:  "Example 3: Events",
		Width:  800,
		Height: 600,
		EnabledEvents: []canvas.Event{
			canvas.MouseDownEvent{},
			canvas.MouseMoveEvent{},
			canvas.TouchStartEvent{},
			canvas.TouchMoveEvent{},
			canvas.KeyDownEvent{},
		},
	})
	if err != nil {
		log.Fatal(err)
	}
}

func run(ctx *canvas.Context) {
	d := &demo{}
	for !d.quit {
		select {
		case event := <-ctx.Events():
			d.handle(event)
		default:
			d.update()
			d.draw(ctx)
			ctx.Flush()
		}
	}
}

type demo struct {
	quit bool
	// ...
}

func (d *demo) handle(event canvas.Event) {
	switch e := event.(type) {
	case canvas.CloseEvent:
		d.quit = true
	case canvas.MouseDownEvent:
		// ...
	case canvas.MouseMoveEvent:
		// ...
	case canvas.TouchStartEvent:
		// ...
   	case canvas.TouchMoveEvent:
		// ...
   	case canvas.KeyDownEvent:
		// ...
	}
}

func (d *demo) update() {
	// ...
}

func (d *demo) draw(ctx *canvas.Context) {
	// ...
}

Note that the canvas.CloseEvent does not have to be explicitly enabled. It is always enabled by default.

Alternatives

2D game engines:

License

This project is free and open source software licensed under the BSD 3-Clause License.

canvas's People

Contributors

dependabot[bot] avatar fzipp avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

canvas's Issues

package embed is not in GOROOT

Hi there,
I was really excited to try this library out but I think you may have missed including a dependency?
I used go get to install this package but I get this error when running the example:

../../go/pkg/mod/github.com/fzipp/[email protected]/serve.go:9:2: package embed is not in GOROOT (/usr/local/go/src/embed)

I looked at serve.go and it imports `- "embed" but i dont see that anywhere in the repo nor the go.mod

User Interface approach

Hi,

I would like to build a simple diagramming application where the bulk of the 2D drawing are done in canvas.

For the UI (I am looking to use buttons, dropdown menus, slider bars, check boxes and text/numeric fields), what libraries would work well with canvas ?

Cheers

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.