So I'm using the normal viewer.html and the following function included in a separate .js:
function selectedPosition()
{
var visible = PDFView.getVisiblePages();
var firstPage = visible.first;
var pageNumber = firstPage.id;
var currentPage = PDFView.pages[pageNumber - 1];
var selecion = window.getSelection();
var range = selecion.getRangeAt(0);
var rect = range.getBoundingClientRect();
var div = $('#viewer');
var viewport = currentPage.getViewport(div.width / currentPage.getViewport(1.0).width); // Uncaught TypeError: undefined is not a function
var point1 = viewport.convertToPdfPoint(rect.left - div.offset().left, rect.top - div.offset().top);
var point2 = viewport.convertToPdfPoint(rect.right - div.offset().left, rect.bottom - div.offset().top);
console.log(point1);
console.log(point2);
}
What the code should do, is calculate the rectangle points with respect to the pdf so that they are independent on scrolling and zoom.
The code is mostly copied from the viewer.js, so why is getViewport not defined?
Trying
console.log(currentPage.getViewport(1.0));
yields the same error. What am I missing?
I think you can use the "viewport" property of the PageView object that is returned by the call to PDFView.pages[pageNumber - 1]. For example:
function selectedPosition()
{
var visible = PDFView.getVisiblePages();
var firstPage = visible.first;
var pageNumber = firstPage.id;
var currentPage = PDFView.pages[pageNumber - 1];
...
//var viewport = currentPage.getViewport(div.width / currentPage.getViewport(1.0).width);
var viewport = currentPage.viewport; // Should return a reference to the viewport for the page
...
}
Related
My brain is not working properly today and I can't seem to figure this out.
I have a RGBA image data stored in an Uint8Array() and need to scale the width only.
var w = 160;
var h = 200;
var depth=4;
var pixels = new Uint8Array(w*h*depth);
I need to scale the pixels array to 320x200 and every attempt I did ended up with a garbled image.
Thanks to Yves Daoust I revisited some of my old attempts at solving this by duplicating every chunk of 4 to the destination, and now I got it working. So thank you Yves :) I do not know what I did wrong earlier.
This is the working code that I ended up with. I'm 100% sure it can be done differently and better, but at this point I am satisfied :)
Utils.prototype.scalePixelsInWidth = function(pixels) {
var w = 320;
var h = 200;
var scanlineWidth = w*4;
var scaledPixels = new Uint8Array(w*h*4);
var a = 0;
for(let row=0;row<h;row++) {
var col2 = 0;
for(let col=0;col<w;col++) {
var srcIndex = col2*4 + (row*(w/2)*4);
var destIndex = col*4 + (row * scanlineWidth);
scaledPixels[destIndex+0] = pixels[srcIndex+0];
scaledPixels[destIndex+1] = pixels[srcIndex+1];
scaledPixels[destIndex+2] = pixels[srcIndex+2];
scaledPixels[destIndex+3] = pixels[srcIndex+3];
a++;
if (a > 1) {
a = 0;
col2++;
}
}
}
return scaledPixels;
}
I'm looking to be able to link the x-labels in a Chart.js bar chart. I've searched pretty thoroughly, and ended up trying to come up with my own solution: because the labels correspond to the bars directly above them and Chart.js has a built in getBarsAtEvent(evt) method, I tried creating an event if the user didn't click on a chart - this new event had pageX and pageY that was directly above the initial click such that if the user had clicked on a label, the new event would simulate a click on the bar graph.
However, calling getBarsAtEvent(createdClickEvent) repeatedly gives me a Uncaught TypeError ("Cannot read property 'getBoundingClientRect' of null"), which must mean that the getBarsAtEvent method, when called on my simulated click, isn't actually returning anything.
Any suggestions or alternate approaches would be greatly appreciated, thanks in advance.
An alternative approach would be to determine the point where the user is actually clicked and based on that calculate which label was clicked. For that you will need some information about the chart created and have to do some calculations.
Below is a way of doing that, and here is a Fiddle with this code/approach. Hope it helps.
$("#canvas").click(
function(evt){
var ctx = document.getElementById("canvas").getContext("2d");
// from the endPoint we get the end of the bars area
var base = myBar.scale.endPoint;
var height = myBar.chart.height;
var width = myBar.chart.width;
// only call if event is under the xAxis
if(evt.pageY > base){
// how many xLabels we have
var count = myBar.scale.valuesCount;
var padding_left = myBar.scale.xScalePaddingLeft;
var padding_right = myBar.scale.xScalePaddingRight;
// calculate width for each label
var xwidth = (width-padding_left-padding_right)/count;
// determine what label were clicked on AND PUT IT INTO bar_index
var bar_index = (evt.offsetX - padding_left) / xwidth;
// don't call for padding areas
if(bar_index > 0 & bar_index < count){
bar_index = parseInt(bar_index);
// either get label from barChartData
console.log("barChartData:" + barChartData.labels[bar_index]);
// or from current data
var ret = [];
for (var i = 0; i < myBar.datasets[0].bars.length; i++) {
ret.push(myBar.datasets[0].bars[i].label)
};
console.log("current data:" + ret[bar_index]);
// based on the label you can call any function
}
}
}
);
I modified iecs's answer to work with chartjs 2.7.1
var that = this;
this.chart = new Chart($("#chart"), {
type: 'bar',
data: {
labels: labels,
datasets: datasets
},
options: {
events: ["mousemove", "mouseout", "click", "touchstart", "touchmove", "touchend"],
onClick: function(e, data) {
var ctx = $("#chart")[0].getContext("2d");
var base = that.chart.chartArea.bottom;
var height = that.chart.chart.height;
var width = that.chart.chart.scales['x-axis-0'].width;
var offset = $('#chart').offset().top - $(window).scrollTop();
if(e.pageY > base + offset){
var count = that.chart.scales['x-axis-0'].ticks.length;
var padding_left = that.chart.scales['x-axis-0'].paddingLeft;
var padding_right = that.chart.scales['x-axis-0'].paddingRight;
var xwidth = (width-padding_left-padding_right)/count;
// don't call for padding areas
var bar_index = (e.offsetX - padding_left - that.chart.scales['y-axis-0'].width) / xwidth;
if(bar_index > 0 & bar_index < count){
bar_index = Math.floor(bar_index);
console.log(bar_index);
}
}
}
}
});
The main differences are:
The newer versions of chartjs use an chart.scales array instead of chart.scale with a bunch of values
I had to subtract chart.scales['y-axis-0'].width from the x offset to get the correct bar_index
I changed parseInt to Math.floor, just personal preference
And if you want the cursor to change when you hover over them, add "hover" to the events array and this to the options:
onHover: function(e) {
var ctx = $("#chart")[0].getContext("2d");
var base = that.chart.chartArea.bottom;
var height = that.chart.chart.height;
var width = that.chart.chart.scales['x-axis-0'].width;
var yOffset = $('#chart').offset().top - $(window).scrollTop();
var xOffset = $('#chart').offset().left - $(window).scrollLeft();
var left = xOffset + that.chart.scales['x-axis-0'].paddingLeft + that.chart.scales['x-axis-0'].left;
var right = xOffset + that.chart.scales['x-axis-0'].paddingRight + that.chart.scales['x-axis-0'].left + width;
if(e.pageY > base + yOffset && e.pageX > left && e.pageX < right){
e.target.style.cursor = 'pointer';
}
else {
e.target.style.cursor = 'default';
}
}
I want to make sure that my instance of fabric.Image is always visible.
It doesn't need to be 100% visible, but a portion of it should always be seen. I've seen a few other questions that are similar and I've based my solution on them, but I haven't found anything that completely solves the problem.
My current solution works when no rotation (angles) have been applied to the image. If the angle is at 0 then this solution is perfect, but once the angle start changing I'm having problems.
this.canvas.on('object:moving', function (e) {
var obj = e.target;
obj.setCoords();
var boundingRect = obj.getBoundingRect();
var max_pad_left_over_width = 50;//obj.currentWidth * .08;
var max_pad_left_over_height = 50;//obj.currentHeight * .08;
var max_top_pos = -(obj.currentHeight - max_pad_left_over_height);
var max_bottom_pos = obj.canvas.height - max_pad_left_over_height;
var max_left_pos = -(obj.currentWidth - max_pad_left_over_width);
var max_right_pos = obj.canvas.width - max_pad_left_over_width;
if(boundingRect.top < max_top_pos) {
obj.setTop(max_top_pos);
}
if(boundingRect.left < max_left_pos){
obj.setLeft(max_left_pos);
}
if(boundingRect.top > max_bottom_pos) {
obj.setTop(max_bottom_pos);
}
if(boundingRect.left > max_right_pos) {
obj.setLeft(max_right_pos);
}
});
I've created an example: https://jsfiddle.net/krio/wg0aL8ef/24/
Notice how when no rotation (angle) is applied you cannot force the image to leave the visible canvas. Now, add some rotation to the image and move it around. How can I get the rotated image to also always stay within view? Thanks.
Fabric.js provides with two object properties isContainedWithinRect and intersectsWithRect which we can use to solve the problem.
An object should not be allowed to go outside the canvas boundaries. This can be achieved if we somehow manage to make sure that the object is either inside the canvas, or at least intersects with the canvas boundaries.
For this case, since you want some portion of the image inside the canvas, we will take a rect smaller than the canvas instead of canvas boundaries to be the containing rect of the image.
Using the properties mentioned to make sure that the above two conditions are satisfied after any movement. Here's how the code looks like:
var canvas = new fabric.Canvas('c');
var imgElement = document.getElementById('my-img');
var imgInstance = new fabric.Image(imgElement, {
top: 0,
left: 0
});
imgInstance.setScaleX(0.6);
imgInstance.setScaleY(0.6);
canvas.add(imgInstance);
var prevLeft = 0,
prevTop = 0;
var max_pad_left_over_width = imgInstance.currentWidth * .08;
var max_pad_left_over_height = imgInstance.currentHeight * .08;
var max_top_pos = max_pad_left_over_height;
var max_bottom_pos = imgInstance.canvas.height - max_pad_left_over_height;
var max_left_pos = max_pad_left_over_width;
var max_right_pos = imgInstance.canvas.width - max_pad_left_over_width;
var pointTL = new fabric.Point(max_left_pos, max_top_pos);
var pointBR = new fabric.Point(max_right_pos, max_bottom_pos);
canvas.on('object:moving', function(e) {
var obj = e.target;
obj.setCoords();
if (!obj.intersectsWithRect(pointTL, pointBR) && !obj.isContainedWithinRect(pointTL, pointBR)) {
obj.setTop(prevTop);
obj.setLeft(prevLeft);
}
prevLeft = obj.left;
prevTop = obj.top;
});
Here's the link to the fiddle: https://jsfiddle.net/1ghvjxbk/ and documentation where these properties are mentioned.
function moveCamera() {
var canvas_holder = document.getElementById("game");
var context = canvas_holder.getContext("2d");
// camera code
// START
// Viewport height
var eye = $("#container").height();
// canvas height
var can_height = $("#canvas").height();
// Initial pos. of car. Multiplied by 30 to convert it into pixels.
// CHECK BETTER FOR CONVERTION
var car_pos = (car.GetWorldCenter().y)*30;
var half_eye = (eye/2);
// Initial Settings of camera
if ((car_pos > half_eye) && (can_height - car_pos) > half_eye){
context.translate(-(car_pos-half_eye),0);
} else if ((can_height - car_pos) < half_eye){
context.translate(-(can_height - eye), 0);
}
var pos = new Array();
var length;
// initial position of object is stored
pos[0] = (car.GetWorldCenter().y)*30;
//move camera
//vector is defined which will store the current y-coordinate
var p = new b2Vec2();
p = (car.GetWorldCenter().y)*30;
pos.push(p);
var length = pos.length;
// in pexels. finds out the distance moved in one timestep
var s = (pos[length-1]-pos[length-2]);
// checks if object is not in eye/2. Thus can be translated
if ((half_eye < (can_height - p)) && (p > half_eye)){
// canvas is translated
context.translate(-s,0);
}
}
so i have the code above and i get a reference error context is not defined.
why is that? i mean i did wrote:
var canvas_holder = document.getElementById("game");
var context = canvas_holder.getContext("2d");
but it looks like it not working... why? what am i missing? can it be that .getContext is not written properly?
I've been googling all day to find a way to align a layer which is converted to Smart Object center to the canvas proceeded by scripting, but haven't found a solution so far. I ended up with the code below, but it doesn't do the job. Could anyone help, please?
var baseFile = new File(openDialog()); //open base JPEG file
var workFile = new File(openDialog()); //open work JPEG file
var baseDoc = app.open(baseFile);
var workDoc = app.open(workFile);
createSO(workDoc.layers[0]);
workDoc.resizeImage(280,280);
workDoc.artLayers[0].duplicate(baseDoc, ElementPlacement.INSIDE);
app.activeDocument = baseDoc;
var Hoffset = (baseDoc.width - workDoc.width) / 2;
var Voffset = (baseDoc.height - workDoc.height) / 2;
baseDoc.layers[1].position = Array(Hoffset, Voffset);
function createSO(){
var doc = app.activeDocument;
var idnewPlacedLayer = stringIDToTypeID( "newPlacedLayer" );
executeAction( idnewPlacedLayer, undefined, DialogModes.NO );
return doc.activeLayer;
}
I think you can simplify a bit in this way:
var baseFile = new File(openDialog()); //open base JPEG file
var workFile = new File(openDialog()); //open work JPEG file
var baseDoc = app.open(baseFile);
var workDoc = app.open(workFile, undefined, true);
workDoc.resizeImage(280, 280);
var duplicated = workDoc.artLayers[0].duplicate(baseDoc, ElementPlacement.INSIDE);
var Hoffset = (baseDoc.width - workDoc.width) / 2;
var Voffset = (baseDoc.height - workDoc.height) / 2;
app.activeDocument = baseDoc;
duplicated.translate(Hoffset, Voffset);
The main point here is using translate method; however you can also avoid to create a createSO function, using the 3rd argument of app.open (that is asSmartObject).