1<!DOCTYPE html> 2<title>TextEdit demo in CanvasKit</title> 3<meta charset="utf-8" /> 4<meta http-equiv="X-UA-Compatible" content="IE=edge"> 5<meta name="viewport" content="width=device-width, initial-scale=1.0"> 6<script type="text/javascript" src="https://particles.skia.org/dist/canvaskit.js"></script> 7<script type="text/javascript" src="textapi_utils.js"></script> 8<script type="text/javascript" src="spiralshader.js"></script> 9 10<style> 11canvas { 12 border: 1px dashed grey; 13} 14</style> 15 16<body> 17 <h1>TextEdit in CanvasKit</h1> 18 19 <canvas id=para2 width=600 height=600 tabindex='-1'></canvas> 20</body> 21 22<script type="text/javascript" charset="utf-8"> 23 let CanvasKit; 24 onload = async () => { 25 CanvasKit = await CanvasKitInit({ locateFile: (file) => 'https://particles.skia.org/dist/'+file }); 26 ParagraphAPI2(); 27 }; 28 29 function ParagraphAPI2() { 30 const surface = CanvasKit.MakeCanvasSurface('para2'); 31 if (!surface) { 32 console.error('Could not make surface'); 33 return; 34 } 35 36 const mouse = MakeMouse(); 37 const cursor = MakeCursor(CanvasKit); 38 const canvas = surface.getCanvas(); 39 const spiralEffect = MakeSpiralShaderEffect(CanvasKit); 40 41 const text0 = "In a hole in the ground there lived a hobbit. Not a nasty, dirty, " + 42 "wet hole full of worms and oozy smells. This was a hobbit-hole and " + 43 "that means good food, a warm hearth, and all the comforts of home."; 44 const LOC_X = 20, 45 LOC_Y = 20; 46 47 const bgPaint = new CanvasKit.Paint(); 48 bgPaint.setColor([0.965, 0.965, 0.965, 1]); 49 50 const editor = MakeEditor(text0, {typeface:null, size:30}, cursor, 540); 51 52 editor.applyStyleToRange({size:130}, 0, 1); 53 editor.applyStyleToRange({italic:true}, 38, 38+6); 54 editor.applyStyleToRange({color:[1,0,0,1]}, 5, 5+4); 55 56 editor.setXY(LOC_X, LOC_Y); 57 58 function drawFrame(canvas) { 59 const lines = editor.getLines(); 60 61 canvas.clear(CanvasKit.WHITE); 62 63 if (mouse.isActive()) { 64 const pos = mouse.getPos(-LOC_X, -LOC_Y); 65 const a = lines_pos_to_index(lines, pos[0], pos[1]); 66 const b = lines_pos_to_index(lines, pos[2], pos[3]); 67 if (a === b) { 68 editor.setIndex(a); 69 } else { 70 editor.setIndices(a, b); 71 } 72 } 73 74 canvas.drawRect(editor.bounds(), bgPaint); 75 76 { 77 // update our animated shaders 78 const rad_scale = Math.sin(Date.now() / 5000) / 2; 79 const shader0 = spiralEffect.makeShader([ 80 rad_scale, 81 editor.width()/2, editor.width()/2, 82 1,0,0,1, // color0 83 0,0,1,1 // color1 84 ]); 85 editor.draw(canvas, [shader0]); 86 shader0.delete(); 87 } 88 89 surface.requestAnimationFrame(drawFrame); 90 } 91 surface.requestAnimationFrame(drawFrame); 92 93 function interact(e) { 94 const type = e.type; 95 if (type === 'pointerup') { 96 mouse.setUp(e.offsetX, e.offsetY); 97 } else if (type === 'pointermove') { 98 mouse.setMove(e.offsetX, e.offsetY); 99 } else if (type === 'pointerdown') { 100 mouse.setDown(e.offsetX, e.offsetY); 101 } 102 }; 103 104 function keyhandler(e) { 105 switch (e.key) { 106 case 'ArrowLeft': editor.moveDX(-1); return; 107 case 'ArrowRight': editor.moveDX(1); return; 108 case 'ArrowUp': 109 e.preventDefault(); 110 editor.moveDY(-1); 111 return; 112 case 'ArrowDown': 113 e.preventDefault(); 114 editor.moveDY(1); 115 return; 116 case 'Backspace': 117 editor.deleteSelection(-1); 118 return; 119 case 'Delete': 120 editor.deleteSelection(1); 121 return; 122 case 'Shift': 123 return; 124 case 'Tab': // todo: figure out how to handle... 125 e.preventDefault(); 126 return; 127 } 128 if (e.ctrlKey) { 129 e.preventDefault(); 130 e.stopImmediatePropagation(); 131 switch (e.key) { 132 case 'r': editor.applyStyleToSelection({color:[1,0,0,1]}); return; 133 case 'g': editor.applyStyleToSelection({color:[0,0.6,0,1]}); return; 134 case 'u': editor.applyStyleToSelection({color:[0,0,1,1]}); return; 135 case 'k': editor.applyStyleToSelection({color:[0,0,0,1]}); return; 136 137 case 's': editor.applyStyleToSelection({shaderIndex:0}); return; 138 139 case 'i': editor.applyStyleToSelection({italic:'toggle'}); return; 140 case 'b': editor.applyStyleToSelection({bold:'toggle'}); return; 141 142 case ']': editor.applyStyleToSelection({size_add:1}); return; 143 case '[': editor.applyStyleToSelection({size_add:-1}); return; 144 case '}': editor.applyStyleToSelection({size_add:10}); return; 145 case '{': editor.applyStyleToSelection({size_add:-10}); return; 146 } 147 } 148 if (!e.ctrlKey && !e.metaKey) { 149 if (e.key.length == 1) { // avoid keys like "Escape" for now 150 e.preventDefault(); 151 e.stopImmediatePropagation(); 152 editor.insert(e.key); 153 } 154 } 155 } 156 157 document.getElementById('para2').addEventListener('pointermove', interact); 158 document.getElementById('para2').addEventListener('pointerdown', interact); 159 document.getElementById('para2').addEventListener('pointerup', interact); 160 document.getElementById('para2').addEventListener('keydown', keyhandler); 161 return surface; 162 } 163 164</script> 165