223
|
1 // http://stackoverflow.com/a/28900478/881731
|
|
2
|
|
3 function WebAssemblyViewport(module, canvasId) {
|
|
4 this.module = module;
|
|
5
|
|
6 // Obtain a reference to the canvas element using its id
|
|
7 this.htmlCanvas = document.getElementById(canvasId);
|
|
8
|
|
9 // Obtain a graphics context on the canvas element for drawing.
|
|
10 this.context = htmlCanvas.getContext('2d');
|
|
11
|
|
12 // ImageData structure that can be used to update the content of the canvas
|
|
13 this.imageData = null;
|
|
14
|
|
15 // Temporary buffer in the WebAssembly heap that is used to render the pixels
|
|
16 this.renderingBuffer = null;
|
|
17
|
|
18 // Get access to the WebAssembly functions
|
|
19 this.ViewportSetSize = this.module.cwrap('ViewportSetSize', null, [ 'number', 'number' ]);
|
|
20 this.ViewportRender = this.module.cwrap('ViewportRender', 'number', [ 'number', 'number', 'number' ]);
|
|
21 this.ViewportMouseDown = this.module.cwrap('ViewportMouseDown', null, [ 'number', 'number', 'number', 'number' ]);
|
|
22 this.ViewportMouseMove = this.module.cwrap('ViewportMouseMove', null, [ 'number', 'number' ]);
|
|
23 this.ViewportMouseUp = this.module.cwrap('ViewportMouseUp', null, [ ]);
|
|
24 this.ViewportMouseEnter = this.module.cwrap('ViewportMouseEnter', null, []);
|
|
25 this.ViewportMouseLeave = this.module.cwrap('ViewportMouseLeave', null, []);
|
|
26 this.ViewportMouseWheel = this.module.cwrap('ViewportMouseWheel', null, [ 'number', 'number', 'number', 'number' ]);
|
|
27 this.ViewportKeyPressed = this.module.cwrap('ViewportKeyPressed', null, [ 'string', 'number', 'number' ]);
|
|
28
|
|
29 this.Redraw = function() {
|
|
30 if (this.imageData === null ||
|
|
31 this.renderingBuffer === null ||
|
|
32 ViewportRender(this.imageData.width,
|
|
33 this.imageData.height,
|
|
34 this.renderingBuffer) == 0) {
|
|
35 console.log('The rendering has failed');
|
|
36 } else {
|
|
37 // Create an accessor to the rendering buffer (i.e. create a
|
|
38 // "window" above the heap of the WASM module), then copy it to
|
|
39 // the ImageData object
|
|
40 this.imageData.data.set(new Uint8ClampedArray(
|
|
41 this.module.buffer,
|
|
42 this.renderingBuffer,
|
|
43 this.imageData.width * this.imageData.height * 4));
|
|
44
|
|
45 this.context.putImageData(imageData, 0, 0);
|
|
46 }
|
|
47 }
|
|
48
|
|
49 this.Resize = function() {
|
|
50 if (this.imageData != null &&
|
|
51 (this.imageData.width != this.window.innerWidth ||
|
|
52 this.imageData.height != this.window.innerHeight)) {
|
|
53 this.imageData = null;
|
|
54 }
|
|
55
|
|
56 this.htmlCanvas.width = window.innerWidth;
|
|
57 this.htmlCanvas.height = window.innerHeight;
|
|
58
|
|
59 if (this.imageData === null) {
|
|
60 this.imageData = context.getImageData(0, 0, this.htmlCanvas.width, this.htmlCanvas.height);
|
|
61 ViewportSetSize(this.htmlCanvas.width, this.htmlCanvas.height);
|
|
62
|
|
63 if (this.renderingBuffer != null) {
|
|
64 this.module._free(this.renderingBuffer);
|
|
65 }
|
|
66
|
|
67 renderingBuffer = this.module._malloc(this.imageData.width * this.imageData.height * 4);
|
|
68 }
|
|
69
|
|
70 this.Redraw();
|
|
71 }
|
|
72
|
|
73 // Force the rendering of the viewport for the first time
|
|
74 this.Resize();
|
|
75
|
|
76 // Register an event listener to call the Resize() function
|
|
77 // each time the window is resized.
|
|
78 window.addEventListener('resize', this.Resize, false);
|
|
79
|
|
80 var that = this;
|
|
81
|
|
82 this.htmlCanvas.addEventListener('contextmenu', function(event) {
|
|
83 // Prevent right click on the canvas
|
|
84 event.preventDefault();
|
|
85 }, false);
|
|
86
|
|
87 this.htmlCanvas.addEventListener('mouseleave', function(event) {
|
|
88 that.ViewportMouseLeave();
|
|
89 });
|
|
90
|
|
91 this.htmlCanvas.addEventListener('mouseenter', function(event) {
|
|
92 that.ViewportMouseEnter();
|
|
93 });
|
|
94
|
|
95 this.htmlCanvas.addEventListener('mousedown', function(event) {
|
|
96 var x = event.pageX - this.offsetLeft;
|
|
97 var y = event.pageY - this.offsetTop;
|
|
98 that.ViewportMouseDown(event.button, x, y, 0 /* TODO */);
|
|
99 });
|
|
100
|
|
101 this.htmlCanvas.addEventListener('mousemove', function(event) {
|
|
102 var x = event.pageX - this.offsetLeft;
|
|
103 var y = event.pageY - this.offsetTop;
|
|
104 that.ViewportMouseMove(x, y);
|
|
105 });
|
|
106
|
|
107 this.htmlCanvas.addEventListener('mouseup', function(event) {
|
|
108 that.ViewportMouseUp();
|
|
109 });
|
|
110
|
|
111 window.addEventListener('keydown', function(event) {
|
|
112 that.ViewportKeyPressed(event.key, event.shiftKey, event.ctrlKey, event.altKey);
|
|
113 });
|
|
114
|
|
115 this.htmlCanvas.addEventListener('wheel', function(event) {
|
|
116 var x = event.pageX - this.offsetLeft;
|
|
117 var y = event.pageY - this.offsetTop;
|
|
118 that.ViewportMouseWheel(event.deltaY, x, y, event.ctrlKey);
|
|
119 event.preventDefault();
|
|
120 });
|
|
121
|
|
122
|
|
123 // Touch events
|
|
124 this.touchTranslation = false;
|
|
125 this.touchZoom = false;
|
|
126
|
|
127 this.ResetTouch = function() {
|
|
128 if (this.touchTranslation ||
|
|
129 this.touchZoom) {
|
|
130 this.ViewportMouseUp();
|
|
131 }
|
|
132
|
|
133 this.touchTranslation = false;
|
|
134 this.touchZoom = false;
|
|
135 }
|
|
136
|
|
137 this.GetTouchTranslation = function(event) {
|
|
138 var touch = event.targetTouches[0];
|
|
139 return [
|
|
140 touch.pageX,
|
|
141 touch.pageY
|
|
142 ];
|
|
143 }
|
|
144
|
|
145 this.GetTouchZoom = function(event) {
|
|
146 var touch1 = event.targetTouches[0];
|
|
147 var touch2 = event.targetTouches[1];
|
|
148 var dx = (touch1.pageX - touch2.pageX);
|
|
149 var dy = (touch1.pageY - touch2.pageY);
|
|
150 var d = Math.sqrt(dx * dx + dy * dy);
|
|
151 return [
|
|
152 (touch1.pageX + touch2.pageX) / 2.0,
|
|
153 (touch1.pageY + touch2.pageY) / 2.0,
|
|
154 d
|
|
155 ];
|
|
156 }
|
|
157
|
|
158 this.htmlCanvas.addEventListener('touchstart', function(event) {
|
|
159 ResetTouch();
|
|
160 });
|
|
161
|
|
162 this.htmlCanvas.addEventListener('touchend', function(event) {
|
|
163 ResetTouch();
|
|
164 });
|
|
165
|
|
166 this.htmlCanvas.addEventListener('touchmove', function(event) {
|
|
167 if (that.touchTranslation.length == 2) {
|
|
168 var t = GetTouchTranslation(event);
|
|
169 that.ViewportMouseMove(t[0], t[1]);
|
|
170 }
|
|
171 else if (that.touchZoom.length == 3) {
|
|
172 var z0 = that.touchZoom;
|
|
173 var z1 = GetTouchZoom(event);
|
|
174 that.ViewportMouseMove(z0[0], z0[1] - z0[2] + z1[2]);
|
|
175 }
|
|
176 else {
|
|
177 // Realize the gesture event
|
|
178 if (event.targetTouches.length == 1) {
|
|
179 // Exactly one finger inside the canvas => Setup a translation
|
|
180 that.touchTranslation = GetTouchTranslation(event);
|
|
181 that.ViewportMouseDown(1 /* middle button */,
|
|
182 that.touchTranslation[0],
|
|
183 that.touchTranslation[1], 0);
|
|
184 } else if (event.targetTouches.length == 2) {
|
|
185 // Exactly 2 fingers inside the canvas => Setup a pinch/zoom
|
|
186 that.touchZoom = GetTouchZoom(event);
|
|
187 var z0 = that.touchZoom;
|
|
188 that.ViewportMouseDown(2 /* right button */,
|
|
189 z0[0],
|
|
190 z0[1], 0);
|
|
191 }
|
|
192 }
|
|
193 });
|
|
194
|
|
195 return this;
|
|
196 }
|
|
197 |