Update:
This small update brings a proper shadow implementation with blurring, two new functions (textPath and textAlongPath) and a few bug fixes. textAlongPath is still somewhat buggy with multi-segment paths but otherwise works great (see the TextWave demo). I fixed a few problems with arcTo that wasn’t working as specified and made a few changes here and there. More importantly, I fixed a nasty crash bug that was sometimes happening on exit.
There is also two new utility functions available from script allowing you to suspend the canvas drawing updates. This allows you to do all the drawing necessary for a frame of animation and refresh the canvas at the end, instead of having it refresh at 30fps while your script is doing all the drawing operations.
With this new API, it was possible to fix all the flickering in the included examples! I’ve also added a few new example scripts. Check out the Polygon example, as well as all the text-related ones.
Download
Every major browser has support for it, Yahoo! Widgets has support for it, how come there isn't something for DesktopX?
Enter DXCanvas, a DesktopX drawing plugin that implements the Canvas spec (plus some DesktopX-specific additions).
How does it work
DXCanvas is a DesktopX drawing plugin, which means it takes over drawing for the object it is associated to.
In the configuration, you can set the initial size for the canvas. By default, the drawing surface size will be 300x150. You can also specify whether you want the surface to be transparent and show the other objects or windows under it, or opaque.
A canvas object is made available to scripting. To be able to draw on the canvas, you need to request a context. This is done by calling getContext(type) on the canvas object. The only supported type right now is "2d".
See the list of functions in the implementation section. Note that some are slightly different from the Canvas specification, or are completely new. This is to adjust to differences in what is possible in a browser and in DesktopX.
Canvas Controller Widget
I've made a little widget to make testing easier. It loads a list of scripts from a user-defined folder and allows you to switch between them. It support scripts written in both JScript and VBScript.
It basically associate the script to an object having the DXCanvas plugin as a capability.
Two functions are required for the scripts to work: Object_OnScriptEnter() and Object_OnScriptExit().
The canvas size is reset its default size of 300x150 when switching between scripts, so make sure to set it to the required size if needed by your script.
To add a new script to the list, create a new .js or .vbs file in a folder and select this folder in the widget preferences. A few test scripts are included in the Script folder.
It also support subfolders (only 1 folder deep), so you can organize your scripts in subfolders.
How to help
Download the Canvas Controller widget and start creating scripts!
What to look for
What is broken/not working properly
Links
Changelog
1.1 Build 287:
1.1 Build 269:
1.0 Build 225:
1.0 Build 217
1.0 Build 214
1.0 Build 211
1.0 Build 201
1.0 Build 191
1.0 Build 180
1.0 Build 168
1.0 Build 159
1.0 Build 149
You can download a test version here. It only includes the Canvas Controller widget for now. Please do not use the DXCanvas plugin in your own objects and widgets yet. This version of the plugin will expire on the 10/01/2008.
Implementation
Under the hood, it's using Cairo, a 2D vector graphics library that also powers the Mozilla and Yahoo Widgets implementations. Right now, the Cairo library is dynamically loaded at runtime, but I hope to have it statically linked into the plugin for the final version.
Here is a list classes with their attributes and functions and the state of their implementation
Canvas
CanvasRenderingContext2D
Gradient
ImageData
PixelArray
TextMetrics
Did you try checking the "reset position" box in preferences?
Well, that's another problem. The only property view I get is for 'Appearance'. I don't get the other tab that allows me to change the script folder location. The earlier builds gave me this tab. I'm not sure what's going on. The dropdown for the Canvas Controller is greyed out and won't drop down. When I select the green refresh icon at the top of the controller, an error dialog pops up and says that "The selected script folder does not exist". When I select the pencil icon, the properties pops up, without the needed tab.
Huh, strange
Can you try to download it again? Extract it in a new folder (ie. not overwriting an old version) and see if it works. I removed some test code (that should not execute when exported as a gadget).
Your latest build fixed the problems I was having. Thanks!!!!
Littleboy,
The following code returns a Syntax error trying to run the script from the Controller. The loadImage function won't take "C:\frame.png". When I change it to "frame.png", the Syntax error is replaced by "Error running script" message: Error# -2147467259 There was an error loading image: frame.png"
frame.png is located at C:\
What am I doing wrong?
---------------------------------------------------------
Sub Load_and_Show Dim ctx Dim img
set ctx = canvas.getContext("2d") img = ctx.loadImage("C:\frame.png") ctx.drawImage img, 100,100
End Sub
------------------------------------------------------
BTW - Thanks for helping me out, and thanks again for putting all of this together.
loadImage returns an object reference, so you need to use Set. This should work:
Thanks! That did it.
Suppose I should read the Canvas Tutorial.
This part is DX-specific
I've been experimenting with your plug-in on an XP machine (at work ) and after you removed the test code you mentioned, my problem with the greyed out drop down and missing tab in the properties disappeared. Last night I downloaded the same build at home and tried it on my Vista machines (Home Premium) - running the latest build of DX and I'm experiencing the same greyed out dropdown and missing property tab. I assume that this is a Vista specific problem. Im not sure what OS you are building the plugin on, but I thought I would let you know what I'm experiencing.
Also..if it helps, I've seen the thread on Vista, SP1, and UAC. I've had UAC turned off forever and never experienced any UAC/permission problems with DX before or after SP1.
New (almost final) build. Flickering should be reduced a bit (I'm no longer asking for redraw every time an operation is done on the canvas). Error handling is also much closer to the canvas spec now (use debugMode if you want to get an error on invalid parameters).
I'll try to see if I can fix the damn flickering for the final version, but otherwise this build should be what will be uploaded on WinCustomize. Let me know if you find any showstopper bugs!
On my todo list for a future version:
Nice work on the latest build. Much faster!
Here are some shape scripts that I put together that might be useful to someone. Some of the procedures already exist in the Canvas list, some don't, but these take away the need to worry about moving the pencil to x,y first and building paths with beginPath and Stroke'ing, (unless you need to build a complex path before drawing it). Also, the procedures that use angles accept degrees, not radians.
'************************************************************************************'Converts angles to radians'************************************************************************************
Function inRadians(angle)Dim PIPI = 3.14159265
inRadians = (angle * PI) / 180End Function
'************************************************************************************'Usage: showImage(context,locx,locy,path)'************************************************************************************
Sub showImage(ctx,x,y,path) Dim img
Set img = ctx.loadImage(path) ctx.drawImage img,x,y
'************************************************************************************'Usage: drawArc(context,locx,locy,radius, startangle - in degrees, endangle - in degrees, direction)' - direction = anticlockwise (true) clockwise (false)'************************************************************************************
Sub drawArc(ctx,x,y,radius,startAngle,endAngle,anticlockwise)Dim startADim endA
startA = inRadians(startAngle) endA = inRadians(endAngle)
ctx.beginPath
ctx.moveTo x,y
ctx.arc x,y,radius,startA,endA, anticlockwise ctx.stroke
'************************************************************************************'Usage: drawPieSlice(context,locx,locy,radius, startangle - in degrees, endangle - in degrees, direction, filled)' - direction = anticlockwise (true) clockwise (false)'************************************************************************************
Sub drawPieSlice(ctx,x,y,radius,startAngle,endAngle,anticlockwise,filled)Dim startADim endA
ctx.beginPath ctx.moveTo x,y ctx.arc x,y,radius,startA,endA, anticlockwise ctx.lineTo x,y ctx.stroke if filled = true then ctx.fill end if
'******************************************************************************'Usage: drawCircle context,centerx,centery, radius, number of segments, filled'******************************************************************************
Sub drawCircle(ctx,cx,cy,radius,num_segments, filled)Const PI = 3.14159265
Dim X Dim Y Dim theta Dim dtheta Dim seg
dtheta = 2 * PI/ num_segments ctx.beginPath ctx.moveto cx+radius, cy theta = 0 For seg = 1 To num_segments theta = theta + dtheta ctx.lineto cx + (radius * Cos(theta)), cy + (radius * Sin(theta)) Next if filled = true then ctx.fill end if ctx.strokeEnd Sub
'******************************************************************************'Usage: drawAngleLine context,startx,starty, length of line, angle in degrees'******************************************************************************
Sub drawAngleLine(ctx,startx,starty,linelength,angle)Const PI = 3.14159265Dim theta theta = (2 * PI/ 360) * angle ctx.beginPath ctx.moveto startx,starty ctx.lineto startx + (linelength * Cos(theta)), starty + (linelength * Sin(theta)) 'drawLine ctx, startx + ((linelength-200) * Cos(theta)), starty + ((linelength-200) * Sin(theta)), startx + (linelength * Cos(theta)), starty + (linelength * Sin(theta)) ctx.strokeEnd Sub
'************************************************************************************'usage : returns distance from point x1,x1 to point x2,y2'************************************************************************************Function distance(x1,y1,x2,y2) distance = sqr( ((x2-x1)*(x2-X1)) + ((y2-y1)*(y2-y1)))End Function
'******************************************************************************'Usage: drawRectangle context,startx,starty,endx,endy, filled'******************************************************************************
Sub drawRectangle(ctx,x1,y1,x2,y2, filled) ctx.beginPath ctx.moveto x1, y1 ctx.rect x1,y1,abs(x2-x1),abs(y2-y1) if filled = true then ctx.fill end if ctx.strokeEnd Sub
'******************************************************************************'Usage: drawGrid context,startx,starty,endx,endy, grid size' - Draws grid by specifying the grid size in pixels'******************************************************************************
Sub drawGrid(ctx,x1,y1,x2,y2,gridsize) Dim L Dim Width Dim Height
Width = ABS(x2-x1) Height = ABS(y2-y1)
For L = 1 to (width+gridsize) step gridsize drawLine ctx,x1+L,y1,x1+L,y2 drawLine ctx,x1,y1+L,x2,y1+L Next drawRectangle ctx,x1,y1,x2,y2,false End Sub
'******************************************************************************'Usage: drawGrid2 context,startx,starty,endx,endy, num of divisions along x, num of divisions along y' - Draws grid by specifying the number of divisions per direction'******************************************************************************
Sub drawGrid2(ctx,x1,y1,x2,y2,xdivisions, ydivisions) Dim L Dim Width Dim Height Dim xgridsize Dim ygridsize
Width = ABS(x2-x1) Height = ABS(y2-y1) xgridsize = round(Width/xdivisions) ygridsize = round(Height/ydivisions) For L = 1 to width step xgridsize drawLine ctx,x1+L,y1,x1+L,y2 Next
For L = 1 to height step ygridsize drawLine ctx,x1,y1+L,x2,y1+L Next
drawRectangle ctx,x1,y1,x2,y2,false End Sub
'******************************************************************************'Usage: draws a line on the x-axis with ticks on it, could be used for graph axis '******************************************************************************
Sub tick_x(ctx,x1,y1,x2,xdivisions,ticklength) Dim L Dim Width Dim xgridsize Width = ABS(x2-x1) xgridsize = round(Width/xdivisions) For L = 0 to width step xgridsize drawLine ctx,x1+L,y1,x1+L,y1-ticklength Next drawLine ctx,x1,y1,x2,y1 End Sub
'******************************************************************************'Usage: draws a line on the y-axis with ticks on it, could be used for graph axis'******************************************************************************
Sub tick_y(ctx,x1,y1,y2,ydivisions,ticklength) Dim L Dim Height Dim ygridsize Height = ABS(y2-y1) ygridsize = round(Height/ydivisions) For L = 0 to height step ygridsize drawLine ctx,x1,y1+L,x1+ticklength,y1+L Next
drawLine ctx,x1,y1,x1,y2 End Sub
'******************************************************************************'Usage: drawEllipse context,centerx,centery, radiuswide, raiustall, number of segments, filled'******************************************************************************
Sub drawEllipse(ctx,cx,cy,radius1,radius2,num_segments, filled)Const PI = 3.14159265
Dim startX Dim startY Dim theta Dim dtheta Dim seg
dtheta = 2 * PI/ num_segments ctx.beginPath ctx.moveto cx+radius1, cy theta = 0 For seg = 1 To num_segments theta = theta + dtheta ctx.lineto cx + ((radius1) * Cos(theta)), cy + (radius2 * Sin(theta)) if seg =1 then startX=cx + (radius1 * Cos(theta)) startY=cy + (radius2 * Sin(theta)) end if Next ctx.lineto startX, StartY if filled = true then ctx.fill end if ctx.stroke
'************************************************************************************'Usage: drawRoundRectangle context,Leftx,Lefty, width, height, corner radius, filled'************************************************************************************
Sub drawRoundRectangle(ctx, x, y, width, height, radius, filled) ctx.beginPath ctx.moveTo x, y + radius ctx.lineTo x, y + height - radius ctx.quadraticCurveTo x , y + height, x + radius, y + height ctx.lineTo x + width - radius, y + height ctx.quadraticCurveTo x + width, y + height, x + width, y + height - radius ctx.lineTo x + width, y + radius ctx.quadraticCurveTo x + width, y, x + width - radius, y ctx.lineTo x + radius, y ctx.quadraticCurveTo x, y, x, y + radius if filled = true then ctx.fill end if ctx.strokeEnd Sub
I'm still having problems with the new build running the Canvas Controller on my Vista machines (see reply #36 of this thread). The controller and canvas procedures/plug-ins work great on my XP machine here at work.
Any ideas?
Thanks.
I haven't had time to look further into that problem yet. I'm using Vista myself and it works fine here. I included a dxpack in the latest build in case you want to take a look. Good work on the scripts. Are you working on a widget already?
I'm going to publish another test build soon as there is a problem with stroked text in the current public build (fixed internally).
I have not started on a widget/object yet. I tried to on Sep 4th when I posted about my problem with the Controller on Vista. After I realized that the Controller would not work, I tried to make an object from scratch, added your plug-in under additional capabilities and tried to run some of the scripts I put together. I could not go further from that point on because I got script errors (Null), "Object not available" type errors and assumed that I either had a Vista problem or was not making a proper declaration somewhere in my script to include the Canvas plug-in. I can examine the scripts in the Dxpack that you included in the build to see what I'm doing wrong. I hope I'm just having a problem with your Canvas Controller widget and not the plug-in itself. I REALY want this plug-in to work and have some project ideas in mind.
Some ideas include:
- Charting and Graphing
- Meters that can't be done with the current plug-ins:
- for example, I just made a DX Wi-Fi meter that works on Vista. It does not use any plug-ins, but uses different bitmaps for different states(meter levels). I want the meter to be dynamically drawn so I'm not limited to discreet states (i.e. 5 states and 5 images)
- DX based image editing
- Live object reflections
- I have not thought much about this yet, but is it possible to pass the .Image value from a normal DX object into the context of an object using the DXcanvas plugin so it can be manipulated or annotated with the drawing subs?
- Realtime enhancements to objects, like running indicators, or annotations to highlight a specific area on an object
- a simple implemantation of the LOGO language(which is fun for kids to play with). I always liked moving the turtle around.
I'm going to work on creating or more accurately adapting the Bar Chart Script (that I mentioned in the forum a while back) into a function. The basic function should be easy, the only issues I've experienced have been parsing csv files because of essentially no standards.
Wouldn't the live object reflection be possible already? Set up a mask object then draw a gradient with one color being transparent and import the image into that object.
Also, I would think you could easily get another object's picture by using Object.Picture? Off to try some of your ideas.
Object.Picture I meant, not Object.Image
Technically yes, object reflection is possible, but the reflected image has to be 'mirrored' to actually represent the reflection. The ImageMagik dll will allow you to do the image manipulation. I meant reflections of stuff drawn on a Canvas object, which is deninately doable.
I love it when DX nerds get together!
Here's a better calendar script.
New Build with a little bit of logging. See DXCanvas.log in AppData folder if using the widget or the currentTheme folder if using the dxpack (don't forget to copy the DXCanvas dll to your DesktopX SDPlugins folder if using the later!).
The problem with stroked text always showing at coordinates (0,0) should be fixed. There is also a slight change with the default text position. If you want to get the old behaviour, set the text baseline to "top" (ctx.textBaseline = "top").
You can also use CSS3 hsl/hsla color definitions now. The format is hsl[a](hue, saturation, lightness [,alpha]) with hue an angle in degrees, saturation and lightness as a percentage. For example, hsla(240, 100%, 50%, 0.5) will get you a semi-transparent solid blue.
Another build is available with a lot of small bugs and a few crashes and leaks fixed. The only new feature is support for % notation with rgb/rgba colors and a few new color definitions.
There are many great features available to you once you register, including:
Sign in or Create Account