Programmatically generate SVG (vector) images, animations, and interactive Jupyter widgets
1javascript = ''' 2require.undef('drawingview'); 3 4define('drawingview', ['@jupyter-widgets/base'], function(widgets) { 5 class DrawingModel extends widgets.DOMWidgetModel { 6 defaults() { 7 return { 8 ...super.defaults(), 9 _model_name: DrawingModel.model_name, 10 _model_module: DrawingModel.model_module, 11 _model_module_version: DrawingModel.model_module_version, 12 _view_name: DrawingModel.view_name, 13 _view_module: DrawingModel.view_module, 14 _view_module_version: DrawingModel.view_module_version, 15 }; 16 } 17 static serializers = { 18 ...widgets.DOMWidgetModel.serializers, 19 }; 20 static model_name = 'DrawingModel'; 21 static model_module = 'drawingview'; 22 static model_module_version = '0.1.0'; 23 static view_name = 'DrawingView'; 24 static view_module = 'drawingview'; 25 static view_module_version = '0.1.0'; 26 } 27 28 class DrawingView extends widgets.DOMWidgetView { 29 render() { 30 this.container = document.createElement('a'); 31 this.image_changed(); 32 this.container.appendChild(this.svg_view); 33 this.el.appendChild(this.container); 34 this.model.on('change:_image', this.image_changed, this); 35 this.model.on('change:_mousemove_blocked', this.block_changed, 36 this); 37 this.model.on('change:frame_delay', this.delay_changed, 38 this); 39 this.model.on('change:_frame_blocked', this.delay_changed, 40 this); 41 this.model.on('change:disable', this.delay_changed, 42 this); 43 this.delay_changed(); 44 } 45 image_changed() { 46 this.container.innerHTML = this.model.get('_image'); 47 this.svg_view = this.container.getElementsByTagName('svg')[0]; 48 this.cursor_point = this.svg_view.createSVGPoint(); 49 this.register_events(); 50 } 51 last_move = null; 52 last_mousemove_blocked = null; 53 last_timer = null; 54 block_changed() { 55 var widget = this; 56 window.setTimeout(function() { 57 if (widget.model.get('_mousemove_blocked') 58 != widget.last_mousemove_blocked && widget.last_move) { 59 widget.send_mouse_event('mousemove', widget.last_move); 60 } 61 }, 0); 62 } 63 send_mouse_event(name, e) { 64 this.last_move = null; 65 if (this.model.get('disable')) { 66 return; 67 } 68 69 this.cursor_point.x = e.clientX; 70 this.cursor_point.y = e.clientY; 71 var svg_pt = this.cursor_point.matrixTransform( 72 this.svg_view.getScreenCTM().inverse()); 73 74 this.send({ 75 name: name, 76 x: svg_pt.x, 77 y: -svg_pt.y, 78 type: e.type, 79 button: e.button, 80 buttons: e.buttons, 81 shiftKey: e.shiftKey, 82 altKey: e.altKey, 83 ctrlKey: e.ctrlKey, 84 metaKey: e.metaKey, 85 clientX: e.clientX, 86 clientY: e.clientY, 87 movementX: e.movementX, 88 movementY: e.movementY, 89 timeStamp: e.timeStamp, 90 targetId: e.target ? e.target.id : null, 91 currentTargetId: e.currentTarget ? e.currentTarget.id : null, 92 relatedTargetId: e.relatedTarget ? e.relatedTarget.id : null, 93 }); 94 } 95 delay_changed() { 96 var widget = this; 97 window.clearTimeout(widget.last_timer); 98 if (widget.model.get('disable')) { 99 return; 100 } 101 var delay = widget.model.get('frame_delay'); 102 if (delay > 0) { 103 widget.last_timer = window.setTimeout(function() { 104 widget.send_timed_event('timed'); 105 }, delay); 106 } 107 } 108 send_timed_event(name) { 109 if (this.model.get('disable')) { 110 return; 111 } 112 113 this.send({ 114 name: name, 115 }); 116 } 117 register_events() { 118 var widget = this; 119 this.svg_view.addEventListener('mousedown', function(e) { 120 e.preventDefault(); 121 widget.send_mouse_event('mousedown', e); 122 }); 123 this.svg_view.addEventListener('mousemove', function(e) { 124 e.preventDefault(); 125 if (widget.model.get('_mousemove_blocked') 126 == widget.last_mousemove_blocked) { 127 widget.last_move = e; 128 } else { 129 widget.send_mouse_event('mousemove', e); 130 } 131 }); 132 this.svg_view.addEventListener('mouseup', function(e) { 133 e.preventDefault(); 134 widget.send_mouse_event('mouseup', e); 135 }); 136 } 137 } 138 139 return { 140 DrawingModel: DrawingModel, 141 DrawingView: DrawingView 142 }; 143}); 144'''