Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ bookclub-shinyui.knit.md
bookclub-shinyui_files
libs
_book/*.html
app.R
app2.R

renv/
renv.lock
Expand Down
492 changes: 458 additions & 34 deletions 15_optimize-your-apps-with-custom-handlers.Rmd

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions DESCRIPTION
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Imports:
bookdown,
bs4Dash,
DiagrammeR,
htmltools,
httpuv,
jsonlite,
purrr,
Expand Down
78 changes: 78 additions & 0 deletions examples/chapter-15/01-handwriting-recognition/app.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
library(shiny)

dropdownDeps <- function(){
htmltools::htmlDependency(name = "handwriting",
version = "1.0.0",
src = c(file = "."),
script = "handwriting.canvas.js")
}


ui = fluidPage(
dropdownDeps(),
htmltools::HTML('

<div>
<span style="display : inline-block">
PenSize <span id="lineWidth">3</span><input type="range" id="penSize" min="1" max="30" value="3">
<br>
<canvas id="canvas" width="400" height="400" style="border: 2px solid; cursor: crosshair;"></canvas>
<br>
<form>
Language:
<select id="language">
<option value="zh_TW" selected="selected">Chinese</option>
<option value="ja">Japanese</option>
<option value="en">English</option>
</select>
</form>
<br>
<button onclick="canvas.erase();">Erase</button>
<button onclick="
var e = document.getElementById(\'language\');
canvas.setOptions({language: e.options[e.selectedIndex].value});
canvas.recognize();
">Send</button>
<button onclick="canvas.undo()">Undo</button>
<button onclick="canvas.redo()">Redo</button>
<br>
<p id="result2">result: <span id="result"></span></p>
</span>
<script type="text/javascript" src="handwriting.canvas.js"></script>
<script type="text/javascript" defer="">
var canvas = new handwriting.Canvas(document.getElementById(\'canvas\'), 3);
var width = document.getElementById("demo").clientWidth
canvas.cxt.canvas.width = width < 400 ? width : 400;
canvas.cxt.canvas.height = width < 400 ? width : 400;
canvas.setCallBack(function(data, err) {
if (err) throw err;
else document.getElementById("result").innerHTML = data;
});
canvas.set_Undo_Redo(true, true);
var penSize = document.getElementById("penSize");
penSize.addEventListener("mousemove", function() {
document.getElementById("lineWidth").innerHTML = penSize.value;
});
penSize.addEventListener("change", function(){
canvas.setLineWidth(penSize.value);
});
</script>
</div>
')
)



server <- function(input, output, session) {

observeEvent(input$myinput,{
removeUI(selector = "#result")

insertUI(selector = "#result2",
ui = paste0(as.character(input$myinput), collapse = " "),
where = "beforeEnd")
})

}

shinyApp(ui, server)
248 changes: 248 additions & 0 deletions examples/chapter-15/01-handwriting-recognition/handwriting.canvas.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
(function(window, document) {

// Establish the root object, `window` (`self`) in the browser,
// or `this` in some virtual machines. We use `self`
// instead of `window` for `WebWorker` support.
var root = typeof self === 'object' && self.self === self && self || this;

// Create a safe reference to the handwriting object for use below.
var handwriting = function(obj) {
if (obj instanceof handwriting) return obj;
if (!(this instanceof handwriting)) return new handwriting(obj);
this._wrapped = obj;
};

root.handwriting = handwriting;

handwriting.Canvas = function(cvs, lineWidth) {
this.canvas = cvs;
this.cxt = cvs.getContext("2d");
this.cxt.lineCap = "round";
this.cxt.lineJoin = "round";
this.lineWidth = lineWidth || 3;
this.width = cvs.width;
this.height = cvs.height;
this.drawing = false;
this.handwritingX = [];
this.handwritingY = [];
this.trace = [];
this.options = {};
this.step = [];
this.redo_step = [];
this.redo_trace = [];
this.allowUndo = false;
this.allowRedo = false;
cvs.addEventListener("mousedown", this.mouseDown.bind(this));
cvs.addEventListener("mousemove", this.mouseMove.bind(this));
cvs.addEventListener("mouseup", this.mouseUp.bind(this));
cvs.addEventListener("touchstart", this.touchStart.bind(this));
cvs.addEventListener("touchmove", this.touchMove.bind(this));
cvs.addEventListener("touchend", this.touchEnd.bind(this));
this.callback = undefined;
this.recognize = handwriting.recognize;
};
/**
* [toggle_Undo_Redo description]
* @return {[type]} [description]
*/
handwriting.Canvas.prototype.set_Undo_Redo = function(undo, redo) {
this.allowUndo = undo;
this.allowRedo = undo ? redo : false;
if (!this.allowUndo) {
this.step = [];
this.redo_step = [];
this.redo_trace = [];
}
};

handwriting.Canvas.prototype.setLineWidth = function(lineWidth) {
this.lineWidth = lineWidth;
};

handwriting.Canvas.prototype.setCallBack = function(callback) {
this.callback = callback;
};

handwriting.Canvas.prototype.setOptions = function(options) {
this.options = options;
};


handwriting.Canvas.prototype.mouseDown = function(e) {
// new stroke
this.cxt.lineWidth = this.lineWidth;
this.handwritingX = [];
this.handwritingY = [];
this.drawing = true;
this.cxt.beginPath();
var rect = this.canvas.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
this.cxt.moveTo(x, y);
this.handwritingX.push(x);
this.handwritingY.push(y);
};


handwriting.Canvas.prototype.mouseMove = function(e) {
if (this.drawing) {
var rect = this.canvas.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
this.cxt.lineTo(x, y);
this.cxt.stroke();
this.handwritingX.push(x);
this.handwritingY.push(y);
}
};

handwriting.Canvas.prototype.mouseUp = function() {
var w = [];
w.push(this.handwritingX);
w.push(this.handwritingY);
w.push([]);
this.trace.push(w);
this.drawing = false;
if (this.allowUndo) this.step.push(this.canvas.toDataURL());
};


handwriting.Canvas.prototype.touchStart = function(e) {
e.preventDefault();
this.cxt.lineWidth = this.lineWidth;
this.handwritingX = [];
this.handwritingY = [];
var de = document.documentElement;
var box = this.canvas.getBoundingClientRect();
var top = box.top + window.pageYOffset - de.clientTop;
var left = box.left + window.pageXOffset - de.clientLeft;
var touch = e.changedTouches[0];
touchX = touch.pageX - left;
touchY = touch.pageY - top;
this.handwritingX.push(touchX);
this.handwritingY.push(touchY);
this.cxt.beginPath();
this.cxt.moveTo(touchX, touchY);
};

handwriting.Canvas.prototype.touchMove = function(e) {
e.preventDefault();
var touch = e.targetTouches[0];
var de = document.documentElement;
var box = this.canvas.getBoundingClientRect();
var top = box.top + window.pageYOffset - de.clientTop;
var left = box.left + window.pageXOffset - de.clientLeft;
var x = touch.pageX - left;
var y = touch.pageY - top;
this.handwritingX.push(x);
this.handwritingY.push(y);
this.cxt.lineTo(x, y);
this.cxt.stroke();
};

handwriting.Canvas.prototype.touchEnd = function(e) {
var w = [];
w.push(this.handwritingX);
w.push(this.handwritingY);
w.push([]);
this.trace.push(w);
if (this.allowUndo) this.step.push(this.canvas.toDataURL());
};

handwriting.Canvas.prototype.undo = function() {
if (!this.allowUndo || this.step.length <= 0) return;
else if (this.step.length === 1) {
if (this.allowRedo) {
this.redo_step.push(this.step.pop());
this.redo_trace.push(this.trace.pop());
this.cxt.clearRect(0, 0, this.width, this.height);
}
} else {
if (this.allowRedo) {
this.redo_step.push(this.step.pop());
this.redo_trace.push(this.trace.pop());
} else {
this.step.pop();
this.trace.pop();
}
loadFromUrl(this.step.slice(-1)[0], this);
}
};

handwriting.Canvas.prototype.redo = function() {
if (!this.allowRedo || this.redo_step.length <= 0) return;
this.step.push(this.redo_step.pop());
this.trace.push(this.redo_trace.pop());
loadFromUrl(this.step.slice(-1)[0], this);
};

handwriting.Canvas.prototype.erase = function() {
this.cxt.clearRect(0, 0, this.width, this.height);
this.step = [];
this.redo_step = [];
this.redo_trace = [];
this.trace = [];
};

function loadFromUrl(url, cvs) {
var imageObj = new Image();
imageObj.onload = function() {
cvs.cxt.clearRect(0, 0, this.width, this.height);
cvs.cxt.drawImage(imageObj, 0, 0);
};
imageObj.src = url;
}

handwriting.recognize = function(trace, options, callback) {
if (handwriting.Canvas && this instanceof handwriting.Canvas) {
trace = this.trace;
options = this.options;
callback = this.callback;
} else if (!options) options = {};
var data = JSON.stringify({
"options": "enable_pre_space",
"requests": [{
"writing_guide": {
"writing_area_width": options.width || this.width || undefined,
"writing_area_height": options.height || this.width || undefined
},
"ink": trace,
"language": options.language || "zh_TW"
}]
});
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function() {
if (this.readyState === 4) {
switch (this.status) {
case 200:
var response = JSON.parse(this.responseText);
var results;
if (response.length === 1) callback(undefined, new Error(response[0]));
else results = response[1][0][1];
if (!!options.numOfWords) {
results = results.filter(function(result) {
return (result.length == options.numOfWords);
});
}
if (!!options.numOfReturn) {
results = results.slice(0, options.numOfReturn);
}
Shiny.setInputValue('myinput', results, {priority: 'event'});
break;
case 403:
callback(undefined, new Error("access denied"));
break;
case 503:
callback(undefined, new Error("can't connect to recognition server"));
break;
}


}
});
xhr.open("POST", "https://www.google.com.tw/inputtools/request?ime=handwriting&app=mobilesearch&cs=1&oe=UTF-8");
xhr.setRequestHeader("content-type", "application/json");
xhr.send(data);
};

})(window, document);
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading