How to move a PIXI DisplayObject? - javascript

I have the following files( using last PIXI.js version):
index.html
<!DOCTYPE HTML>
<html>
<head>
<title>pixi.js example 1</title>
<style>
body {
margin: 0;
padding: 0;
background-color: #000000;
width:100%;
height:100%;
}
canvas {
display:block;
}
</style>
<script src="js/pixi.js"></script>
<script src="js/XALUI/XALUI.js"></script>
<script src="js/XALUI/Button.js"></script>
</head>
<body>
<script>
// create an new instance of a pixi stage
var stage = new PIXI.Stage(0x66FF99);
// create a renderer instance
var renderer = PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight);
// add the renderer view element to the DOM
document.body.appendChild(renderer.view);
requestAnimFrame( animate );
// create a texture from an image path
var texture = PIXI.Texture.fromImage("bunny.png");
// XALUI
var button = new XALUI.Button(texture);
stage.addChild(button);
function animate() {
button.position.x = button.position.x + 20;
// render the stage
renderer.render(stage);
requestAnimFrame( animate );
}
</script>
</body>
</html>
js/XALUI/Button.js
XALUI.Button = function(texture) {
PIXI.DisplayObject.call(this);
this._button = new PIXI.Sprite(texture);
}
XALUI.Button.prototype = Object.create(PIXI.DisplayObject.prototype);
XALUI.Button.prototype.constructor = XALUI.Button;
XALUI.Button.prototype._renderWebGL = function(renderSession) {
this._button._renderWebGL(renderSession);
}
XALUI.Button.prototype._renderCanvas = function(renderSession) {
this._button._renderCanvas(renderSession);
};
How can I move my button to another position or to an another initial position? I have tried setting the position.x:
var button = new XALUI.Button(texture);
button.position.x = 100;
And also tried to set the position of the sprite:
this._button = new PIXI.Sprite(texture);
this._button.position.x = 100;
But it doesn't work. Please help.

The problem lies in that you're attaching and moving XALUI.Button instead of the inner PIXI.Sprite. Setting the position on the inner sprite (this._button.position.x) would only work if you had added that PIXI.Sprite to the stage. Since DisplayObjects do not contain multiple DisplayObjects, the stage has no reference to the actual sprite. There are a couple of ways to fix this. First, you could inherit from PIXI.Sprite
XALUI.Button = function(texture) {
PIXI.Sprite.call(this, texture);
};
XALUI.Button.prototype = Object.create(PIXI.Sprite.prototype);
XALUI.Button.prototype.constructor = XALUI.Button;
Or to have it inherit from DisplayObjectContainer in the same manner as above but that implies a button will hold multiple graphical objects so do what works best for you. Here's a working example of the first approach.
var XALUI = {};
// Inherits from PIXI.Sprite
XALUI.Button = function(texture) {
PIXI.Sprite.call(this, texture);
};
XALUI.Button.prototype = Object.create(PIXI.Sprite.prototype);
XALUI.Button.prototype.constructor = XALUI.Button;
// Prepare the stage
var stage = new PIXI.Stage(0x66FF99);
var renderer = PIXI.autoDetectRenderer(window.innerWidth, window.innerHeight);
var texture = PIXI.Texture.fromImage("http://www.goodboydigital.com/pixijs/examples/1/bunny.png");
var button = new XALUI.Button(texture);
document.body.appendChild(renderer.view);
requestAnimationFrame(animate);
stage.addChild(button);
// Animate the button
function animate() {
button.position.x = button.position.x + 5;
if (button.position.x > window.innerWidth + button.width) {
button.position.x = -button.width;
}
renderer.render(stage);
requestAnimationFrame(animate);
}
<script src="https://cdn.rawgit.com/GoodBoyDigital/pixi.js/v2.0.0/bin/pixi.js"></script>

Related

hyperlinking CSS3D object in three.js

My goal is to be able to create a navbar in the three.js environment. Iv set up all the CSS3D appropriately and have set up a mouse down event listener but when it comes to intersects nothing comes up.
//CSS3D Scene
scene2 = new THREE.Scene();
//HTML
element = document.getElementById('dialogueBox');
shop = document.getElementById('shop');
FREEkxxx = document.getElementById('FREEkxxx');
//card
card = new THREE.CSS3DObject(element);
card.position.x = 0;
card.position.y = 0;
card.position.z = 0;
scene2.add(card);
//shop link
shopNav = new THREE.CSS3DObject(shop);
shopNav.position.x = -250;
shopNav.position.y = 250;
shopNav.position.z = 0;
shopNav.userData = {
URL: "http://stackoverflow.com",
name: "shopNav"
};
scene2.add(shopNav);
// videolink
FREEkNav = new THREE.CSS3DObject(FREEkxxx);
FREEkNav.position.x = 220;
FREEkNav.position.y = 250;
FREEkNav.position.z = 0;
FREEkNav.userData = {
URL: "http://stackoverflow.com",
name: "FREEkNav"
};
scene2.add(FREEkNav);
// grouping the nav objects
nav = new THREE.Object3D();
nav.add(shopNav);
nav.add(FREEkNav);
nav.userData = {
URL: "http://stackoverflow.com",
name: "FREEkNav"
};
scene2.add(nav);
document.addEventListener('mousedown', onDocumentMouseDown, false);
var raycaster = new THREE.Raycaster(),
INTERSECTED;
var mouse = new THREE.Vector2();
function onDocumentMouseDown(event) {
event.preventDefault();
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
intersects = raycaster.intersectObjects(nav.children);
console.log(intersects)
if (raycaster.intersects === shopNav) {
window.open(link1)
} else if (raycaster.intersects === FREEkNav) {
window.open(link2)
}
};
I'v grouped the 2 nav elements using THREE.Object3D(); so i assumed i could intersect using nav.children but when you console.log intersects and click one the nav elements you get nothing / [].
There's no need to do raycasting on CSS3D Objects. After all, a CSS3DObject is still an HTML element, just with a more complex CSS transformMatrix() property. You can add event listeners to it like any other HTML element, as it's still part of the DOM:
var element = document.getElementById('dialogueBox');
// Add click listener as usual
element.addEventListener("click", function(event){
console.log("I've been clicked!");
console.log(event);
});
//card
card = new THREE.CSS3DObject(element);
card.position.x = 0;
card.position.y = 0;
card.position.z = 0;
scene2.add(card);
You only need to do raycasting when dealing with THREE.Mesh, THREE.Point or other WebGL-based elements that live inside the <canvas> context.

Createjs: Clone shape when clicking button

I'm trying to clone a shape (square) every time I click a button. I also want to be able to drag the shapes around the canvas. This is my current code:
-inline script
var stage;
var update = true;
var count = 1;
function init(){
stage = new createjs.Stage('myCanvas');
var rect = new createjs.Shape();
rect.graphics.beginFill('#000000').drawRect(0,0,50,50);
rect.name = "rect_" + count;
count++;
var rectName = rect.name
var dragger = new createjs.Container(rectName);
dragger.x = dragger.y = 100;
dragger.addChild(rect);
stage.addChild(dragger);
dragger.on("pressmove",function(evt) {
// currentTarget will be the container that the event listener was added to:
evt.currentTarget.x = evt.stageX;
evt.currentTarget.y = evt.stageY;
// make sure to redraw the stage to show the change:
stage.update();
});
stage.update();
}
$(function(){
$('button').click(function(){
console.log('fire!');
init();
})
})
-file.html
<body onload='init()'>
<canvas id='myCanvas' width='500' height='500'></canvas>
<button class='makeBox'>New Box!</button>
</body>
As you can see I can create a shape and drag it around. But when I click the button it refreshes the shape instead of cloning it. I read this post but I was still a little confused on how to implement cloning.
duplicating objects with easeljs
Can someone please point me in the right direction. If you want bonus points make it so the shapes don't overlap each other. I would appreciate it so much :)
Please have a look at this running code snippet. Hope it will help you a bit.
var stage;
var update = true;
var count = 1;
var rect = null;
var draggerCord = 0;
function init(refRect){
if(refRect)
{
rect = refRect;
}
else
{
stage = new createjs.Stage('myCanvas');
rect = new createjs.Shape();
}
rect.graphics.beginFill('#000000').drawRect(0,0,50,50);
rect.name = "rect_" + count;
count++;
var rectName = rect.name
var dragger = new createjs.Container(rectName);
dragger.x = dragger.y = 100 + draggerCord;
draggerCord +=50;
if(draggerCord > 500)
draggerCord = 100;
dragger.addChild(rect);
stage.addChild(dragger);
dragger.on("pressmove",function(evt) {
// currentTarget will be the container that the event listener was added to:
evt.currentTarget.x = evt.stageX;
evt.currentTarget.y = evt.stageY;
// make sure to redraw the stage to show the change:
stage.update();
});
stage.update();
}
function cloneRect(){
var rectClone = rect.clone();
init(rectClone);
}
$(function(){
$('button').click(function(){
cloneRect();
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="https://code.createjs.com/createjs-2015.11.26.min.js"></script>
<body onload='init()'>
<canvas id='myCanvas' width='500' height='500'></canvas>
<button class='makeBox'>New Box!</button>
</body>

Adding a button to change mesh in babylon.js

I'm having a terrible time trying to show/hide a mesh in my babylon.js scene with a button. I assumed I could create a function that would show/hide meshes and then call said button from my page to affect my scene and wrote the following:
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="assets/js/babylon.js"></script>
<script src="assets/js/hand-1.3.8.js"></script>
<script src="assets/js/cannon.js"></script> <!-- optional physics engine -->
</head>
<body>
<div id="container">
<!-- Page Content -->
<div id="content">
<div id="tableBuilder">
<div id='cssmenu'>
<ul id="tops">
<button type="button" onclick="showTop('T115')">Round</button>
<button type="button" onclick="showTop('T345')">Rectangular</button>
</ul>
</div>
<canvas id="renderCanvas"></canvas>
</div>
</div>
</div>
<script>
if (BABYLON.Engine.isSupported()) {
var myNewScene = null;
var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);
BABYLON.SceneLoader.Load("", "test.babylon", engine, function (newScene) {
myNewScene = newScene;
var meshT115 = newScene.getMeshByName("T115").visibility = 0;
var mesh510 = newScene.getMeshByName("510").visibility = 1;
var meshT345 = newScene.getMeshByName("T345").visibility = 1;
var mesh350 = newScene.getMeshByName("350").visibility = 0;
var myBaseMesh = newScene.getMeshByName("510");
var materialMyMesh = new BABYLON.StandardMaterial("texture1", newScene);
materialMyMesh.diffuseTexture = new BABYLON.Texture("assets/images/stain/Natural.jpg", newScene);
materialMyMesh.specularPower = 50;
myBaseMesh.material = materialMyMesh;
var myTopMesh = newScene.getMeshByName("T345");
var materialMyMesh = new BABYLON.StandardMaterial("texture2", newScene);
materialMyMesh.diffuseTexture = new BABYLON.Texture("assets/images/stain/Natural.jpg", newScene);
materialMyMesh.specularPower = 50;
myTopMesh.material = materialMyMesh;
// Wait for textures and shaders to be ready
newScene.executeWhenReady(function () {
// Attach camera to canvas inputs
var myCamera = new BABYLON.ArcRotateCamera("ArcRotateCamera", 1.2, 1.2, 5, new BABYLON.Vector3(0, 0.1, 0.1), newScene);
myCamera.wheelPrecision = 10;
var light0 = new BABYLON.HemisphericLight("Hemi0", new BABYLON.Vector3(0, 50, 0), newScene);
light0.diffuse = new BABYLON.Color3(1, 1, 1);
light0.specular = new BABYLON.Color3(1, 1, 1);
light0.groundColor = new BABYLON.Color3(.5, .5, .5);
newScene.activeCamera = myCamera;
newScene.activeCamera.attachControl(canvas);
// Once the scene is loaded, just register a render loop to render it
engine.runRenderLoop(function() {
newScene.render();
});
});
}, function (progress) {
// To do: give progress feedback to user
});
}
function showTop (x) {
myNewScene.getMeshById("myTopMesh").visibility = 0;
myNewScene.getMeshById(x).visibility = 1;
}
</script>
</body>
</html>
However, all I get is an Error.
Uncaught TypeError: undefined is not a function
showTop 3d.php::88
onclick 3d.php::88
Line 88 is:
myNewScene.getMeshById(x).visibility = 1;
My question is, if I am declaring myNewScene as a global variable and then assigning it within my scene creation why does my browser think it should be a function?
Thanks.
change this:
if (BABYLON.Engine.isSupported()) {
var myNewScene = null;
var canvas = document.getElementById("renderCanvas");
var engine = new BABYLON.Engine(canvas, true);
to something like this:
var myNewScene, canvas, engine;
if (BABYLON.Engine.isSupported()) {
myNewScene = null;
canvas = document.getElementById("renderCanvas");
engine = new BABYLON.Engine(canvas, true);

Adding more images to the canvas to drag and drop

I want to be able to spawn a new image to the canvas at the click of a button, rather than having to manually edit the code.
I have the following HTML5/JavaScript code that allows images to be dragged and dropped between multiple canvases and it works perfectly for what I require.
What I am doing:
<canvas style="float: left" height="125" width="400" id="cvs1">[No canvas support]</canvas>
<canvas style="float: left; margin-left: 100px" height="125" width="400" id="cvs2">[No canvas support]</canvas>
<script src="http://www.rgraph.net/libraries/RGraph.common.core.js" ></script>
<script>
window.onload = function ()
{
var canvas1 = document.getElementById("cvs1");
var canvas2 = document.getElementById("cvs2");
var context1 = canvas1.getContext('2d');
var context2 = canvas2.getContext('2d');
var imageXY = {x: 5, y: 5};
/**
* This draws the image to the canvas
*/
function Draw ()
{
//Clear both canvas first
canvas1.width = canvas1.width
canvas2.width = canvas2.width
//Draw a red rectangle around the image
if (state && state.dragging) {
state.canvas.getContext('2d').strokeStyle = 'red';
state.canvas.getContext('2d').strokeRect(imageXY.x - 2.5,
imageXY.y - 2.5,
state.image.width + 5,
state.image.height + 5);
}
// Now draw the image
state.canvas.getContext('2d').drawImage(state.image, imageXY.x, imageXY.y);
}
canvas2.onclick =
canvas1.onclick = function (e)
{
if (state && state.dragging) {
state.dragging = false;
Draw();
return;
}
var mouseXY = RGraph.getMouseXY(e);
state.canvas = e.target;
if ( mouseXY[0] > imageXY.x
&& mouseXY[0] < (imageXY.x + state.image.width)
&& mouseXY[1] > imageXY.y
&& mouseXY[1] < (imageXY.y + state.image.height)) {
state.dragging = true;
state.originalMouseX = mouseXY[0];
state.originalMouseY = mouseXY[1];
state.offsetX = mouseXY[0] - imageXY.x;
state.offsetY = mouseXY[1] - imageXY.y;
}
}
canvas1.onmousemove =
canvas2.onmousemove = function (e)
{
if (state.dragging) {
state.canvas = e.target;
var mouseXY = RGraph.getMouseXY(e);
// Work how far the mouse has moved since the mousedon event was triggered
var diffX = mouseXY[0] - state.originalMouseX;
var diffY = mouseXY[1] - state.originalMouseY;
imageXY.x = state.originalMouseX + diffX - state.offsetX;
imageXY.y = state.originalMouseY + diffY - state.offsetY;
Draw();
e.stopPropagation();
}
}
/**
* Load the image on canvas1 initially and set the state up with some defaults
*/
state = {}
state.dragging = false;
state.canvas = document.getElementById("cvs1");
state.image = new Image();
state.image.src = 'http://www.rgraph.net/images/logo.png';
state.offsetX = 0;
state.offsetY = 0;
state.image.onload = function ()
{
Draw();
}
}
</script>
This can also be seen on this JS Fiddle (Note: you have to click the image before you can drag it)
The problem I am having:
I would like to add more images to the canvases so that any of the images can then be dragged and dropped between however many canvases I choose to create.
I can quite easily add more canvases to the page to drag and drop between, however when it comes to adding/spawning more images to the canvases I cannot get it to work.
The only way I can think of being able to do this is by repeating the Draw() function for every single image that gets added. That would mean if I wanted 30 images to be able to drag and drop between 10 different canvases for example, I would need to repeat the Draw() function 30 times. Surely that cannot be the best way to do this?
Unless I am missing something very obvious I cannot see another way of doing this?
Here’s how to configure your code to create multiple objects and click-drag between canvases.
Demo: http://jsfiddle.net/m1erickson/Bnb6A/
Code an object factory function that creates new draggable objects and put all new objects in an array.
Keep this information about each draggable object:
dragging (true/false) indicating if this object is currently being dragged
image: the image for this object
x,y: the current top,left position of this object
width,height: the size of this object
offsetX,offsetY: indicates where the mouse clicked relative to top,left of this object
draw: (a function) that allows this object to draw itself (surrounded by a red rect if it’s being dragged)
On click:
Get the mouse position
Clear both canvases
Iterate through the object array
If an object is being dragged, unset the dragging flag
If an object is now selected, set the dragging flag and set the offsetX,offsetY for the starting mouse position relative to this object.
Redraw all objects
On mousemove:
Get the mouse position
Clear both canvases
Iterate through the object array
If an object is being dragged, move it to the mouse position (setting x,y adjusted for the offset)
Redraw all objects
Code:
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
canvas{ border: 1px solid #808080; }
</style>
<script>
$(function(){
var canvas1 = document.getElementById("cvs1");
var canvas2 = document.getElementById("cvs2");
var contexts=[];
contexts.push(canvas1.getContext('2d'));
contexts.push(canvas2.getContext('2d'));
function clearAll(){
//Clear both canvas first
canvas1.width = canvas1.width
canvas2.width = canvas2.width
}
canvas1.onclick=function(e){ handleClick(e,1); };
canvas2.onclick=function(e){ handleClick(e,2); };
//
function handleClick(e,contextIndex){
e.stopPropagation();
var mouseX=parseInt(e.clientX-e.target.offsetLeft);
var mouseY=parseInt(e.clientY-e.target.offsetTop);
clearAll();
for(var i=0;i<states.length;i++){
var state=states[i];
if(state.dragging){
state.dragging=false;
state.draw();
continue;
}
if ( state.contextIndex==contextIndex
&& mouseX>state.x && mouseX<state.x+state.width
&& mouseY>state.y && mouseY<state.y+state.height)
{
state.dragging=true;
state.offsetX=mouseX-state.x;
state.offsetY=mouseY-state.y;
state.contextIndex=contextIndex;
}
state.draw();
}
}
canvas1.onmousemove = function(e){ handleMousemove(e,1); }
canvas2.onmousemove = function(e){ handleMousemove(e,2); }
//
function handleMousemove(e,contextIndex){
e.stopPropagation();
var mouseX=parseInt(e.clientX-e.target.offsetLeft);
var mouseY=parseInt(e.clientY-e.target.offsetTop);
clearAll();
for(var i=0;i<states.length;i++){
var state=states[i];
if (state.dragging) {
state.x = mouseX-state.offsetX;
state.y = mouseY-state.offsetY;
state.contextIndex=contextIndex;
}
state.draw();
}
}
var states=[];
var img=new Image();
img.onload=function(){
states.push(addState(0,0,img));
}
img.src="http://www.rgraph.net/images/logo.png";
function addState(x,y,image){
state = {}
state.dragging=false;
state.contextIndex=1;
state.image=image;
state.x=x;
state.y=y;
state.width=image.width;
state.height=image.height;
state.offsetX=0;
state.offsetY=0;
state.draw=function(){
var context=contexts[this.contextIndex-1];
if (this.dragging) {
context.strokeStyle = 'red';
context.strokeRect(this.x,this.y,this.width+5,this.height+5)
}
context.drawImage(this.image,this.x,this.y);
}
state.draw();
return(state);
}
$("#more").click(function(){
states.push(addState(states.length*100,0,img));
});
}); // end $(function(){});
</script>
</head>
<body>
<button id="more">Add Image</button><br>
<canvas height="125" width="300" id="cvs1">[No canvas support]</canvas><br>
<canvas height="125" width="300" id="cvs2">[No canvas support]</canvas>
</body>
</html>

How to display whole PDF (not only one page) with PDF.JS?

I've created this demo:
http://polishwords.com.pl/dev/pdfjs/test.html
It displays one page. I would like to display all pages. One below another, or place some buttons to change page or even better load all standard controls of PDF.JS like in Firefox. How to acomplish this?
PDFJS has a member variable numPages, so you'd just iterate through them. BUT it's important to remember that getting a page in pdf.js is asynchronous, so the order wouldn't be guaranteed. So you'd need to chain them. You could do something along these lines:
var currPage = 1; //Pages are 1-based not 0-based
var numPages = 0;
var thePDF = null;
//This is where you start
PDFJS.getDocument(url).then(function(pdf) {
//Set PDFJS global object (so we can easily access in our page functions
thePDF = pdf;
//How many pages it has
numPages = pdf.numPages;
//Start with first page
pdf.getPage( 1 ).then( handlePages );
});
function handlePages(page)
{
//This gives us the page's dimensions at full scale
var viewport = page.getViewport( 1 );
//We'll create a canvas for each page to draw it on
var canvas = document.createElement( "canvas" );
canvas.style.display = "block";
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
//Draw it on the canvas
page.render({canvasContext: context, viewport: viewport});
//Add it to the web page
document.body.appendChild( canvas );
//Move to next page
currPage++;
if ( thePDF !== null && currPage <= numPages )
{
thePDF.getPage( currPage ).then( handlePages );
}
}
Here's my take. Renders all pages in correct order and still works asynchronously.
<style>
#pdf-viewer {
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.1);
overflow: auto;
}
.pdf-page-canvas {
display: block;
margin: 5px auto;
border: 1px solid rgba(0, 0, 0, 0.2);
}
</style>
<script>
url = 'https://github.com/mozilla/pdf.js/blob/master/test/pdfs/tracemonkey.pdf';
var thePdf = null;
var scale = 1;
PDFJS.getDocument(url).promise.then(function(pdf) {
thePdf = pdf;
viewer = document.getElementById('pdf-viewer');
for(page = 1; page <= pdf.numPages; page++) {
canvas = document.createElement("canvas");
canvas.className = 'pdf-page-canvas';
viewer.appendChild(canvas);
renderPage(page, canvas);
}
});
function renderPage(pageNumber, canvas) {
thePdf.getPage(pageNumber).then(function(page) {
viewport = page.getViewport({ scale: scale });
canvas.height = viewport.height;
canvas.width = viewport.width;
page.render({canvasContext: canvas.getContext('2d'), viewport: viewport});
});
}
</script>
<div id='pdf-viewer'></div>
The pdfjs-dist library contains parts for building PDF viewer. You can use PDFPageView to render all pages. Based on https://github.com/mozilla/pdf.js/blob/master/examples/components/pageviewer.html :
var url = "https://cdn.mozilla.net/pdfjs/tracemonkey.pdf";
var container = document.getElementById('container');
// Load document
PDFJS.getDocument(url).then(function (doc) {
var promise = Promise.resolve();
for (var i = 0; i < doc.numPages; i++) {
// One-by-one load pages
promise = promise.then(function (id) {
return doc.getPage(id + 1).then(function (pdfPage) {
// Add div with page view.
var SCALE = 1.0;
var pdfPageView = new PDFJS.PDFPageView({
container: container,
id: id,
scale: SCALE,
defaultViewport: pdfPage.getViewport(SCALE),
// We can enable text/annotations layers, if needed
textLayerFactory: new PDFJS.DefaultTextLayerFactory(),
annotationLayerFactory: new PDFJS.DefaultAnnotationLayerFactory()
});
// Associates the actual page with the view, and drawing it
pdfPageView.setPdfPage(pdfPage);
return pdfPageView.draw();
});
}.bind(null, i));
}
return promise;
});
#container > *:not(:first-child) {
border-top: solid 1px black;
}
<link href="https://npmcdn.com/pdfjs-dist/web/pdf_viewer.css" rel="stylesheet"/>
<script src="https://npmcdn.com/pdfjs-dist/web/compatibility.js"></script>
<script src="https://npmcdn.com/pdfjs-dist/build/pdf.js"></script>
<script src="https://npmcdn.com/pdfjs-dist/web/pdf_viewer.js"></script>
<div id="container" class="pdfViewer singlePageView"></div>
The accepted answer is not working anymore (in 2021), due to the API change for var viewport = page.getViewport( 1 ); to var viewport = page.getViewport({scale: scale});, you can try the full working html as below, just copy the content below to a html file, and open it:
<html>
<head>
<script src="https://mozilla.github.io/pdf.js/build/pdf.js"></script>
<head>
<body>
</body>
<script>
var url = 'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/web/compressed.tracemonkey-pldi-09.pdf';
// Loaded via <script> tag, create shortcut to access PDF.js exports.
var pdfjsLib = window['pdfjs-dist/build/pdf'];
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://mozilla.github.io/pdf.js/build/pdf.worker.js';
var currPage = 1; //Pages are 1-based not 0-based
var numPages = 0;
var thePDF = null;
//This is where you start
pdfjsLib.getDocument(url).promise.then(function(pdf) {
//Set PDFJS global object (so we can easily access in our page functions
thePDF = pdf;
//How many pages it has
numPages = pdf.numPages;
//Start with first page
pdf.getPage( 1 ).then( handlePages );
});
function handlePages(page)
{
//This gives us the page's dimensions at full scale
var viewport = page.getViewport( {scale: 1.5} );
//We'll create a canvas for each page to draw it on
var canvas = document.createElement( "canvas" );
canvas.style.display = "block";
var context = canvas.getContext('2d');
canvas.height = viewport.height;
canvas.width = viewport.width;
//Draw it on the canvas
page.render({canvasContext: context, viewport: viewport});
//Add it to the web page
document.body.appendChild( canvas );
var line = document.createElement("hr");
document.body.appendChild( line );
//Move to next page
currPage++;
if ( thePDF !== null && currPage <= numPages )
{
thePDF.getPage( currPage ).then( handlePages );
}
}
</script>
</html>
The following answer is a partial answer targeting anyone trying to get a PDF.js to display a whole PDF in 2019, as the api has changed significantly. This was of course the OP's primary concern. inspiration sample code
Please take note of the following:
extra libs are being used -- Lodash (for range() function) and polyfills (for promises)....
Bootstrap is being used
<div class="row">
<div class="col-md-10 col-md-offset-1">
<div id="wrapper">
</div>
</div>
</div>
<style>
body {
background-color: #808080;
/* margin: 0; padding: 0; */
}
</style>
<link href="//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.1.266/pdf_viewer.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.1.266/pdf.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.1.266/pdf_viewer.js"></script>
<script src="//cdn.polyfill.io/v2/polyfill.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.js"></script>
<script>
$(document).ready(function () {
// startup
});
'use strict';
if (!pdfjsLib.getDocument || !pdfjsViewer.PDFViewer) {
alert("Please build the pdfjs-dist library using\n" +
" `gulp dist-install`");
}
var url = '//www.pdf995.com/samples/pdf.pdf';
pdfjsLib.GlobalWorkerOptions.workerSrc =
'//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.1.266/pdf.worker.js';
var loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then(function(pdf) {
// please be aware this uses .range() function from lodash
var pagePromises = _.range(1, pdf.numPages).map(function(number) {
return pdf.getPage(number);
});
return Promise.all(pagePromises);
}).then(function(pages) {
var scale = 1.5;
var canvases = pages.forEach(function(page) {
var viewport = page.getViewport({ scale: scale, }); // Prepare canvas using PDF page dimensions
var canvas = document.createElement('canvas');
canvas.height = viewport.height;
canvas.width = viewport.width; // Render PDF page into canvas context
var canvasContext = canvas.getContext('2d');
var renderContext = {
canvasContext: canvasContext,
viewport: viewport
};
page.render(renderContext).promise.then(function() {
if (false)
return console.log('Page rendered');
});
document.getElementById('wrapper').appendChild(canvas);
});
},
function(error) {
return console.log('Error', error);
});
</script>
If you want to render all pages of pdf document in different canvases, all one by one synchronously this is kind of solution:
index.html
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>PDF Sample</title>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="pdf.js"></script>
<script type="text/javascript" src="main.js">
</script>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body id="body">
</body>
</html>
main.css
canvas {
display: block;
}
main.js
$(function() {
var filePath = "document.pdf";
function Num(num) {
var num = num;
return function () {
return num;
}
};
function renderPDF(url, canvasContainer, options) {
var options = options || {
scale: 1.5
},
func,
pdfDoc,
def = $.Deferred(),
promise = $.Deferred().resolve().promise(),
width,
height,
makeRunner = function(func, args) {
return function() {
return func.call(null, args);
};
};
function renderPage(num) {
var def = $.Deferred(),
currPageNum = new Num(num);
pdfDoc.getPage(currPageNum()).then(function(page) {
var viewport = page.getViewport(options.scale);
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var renderContext = {
canvasContext: ctx,
viewport: viewport
};
if(currPageNum() === 1) {
height = viewport.height;
width = viewport.width;
}
canvas.height = height;
canvas.width = width;
canvasContainer.appendChild(canvas);
page.render(renderContext).then(function() {
def.resolve();
});
})
return def.promise();
}
function renderPages(data) {
pdfDoc = data;
var pagesCount = pdfDoc.numPages;
for (var i = 1; i <= pagesCount; i++) {
func = renderPage;
promise = promise.then(makeRunner(func, i));
}
}
PDFJS.disableWorker = true;
PDFJS.getDocument(url).then(renderPages);
};
var body = document.getElementById("body");
renderPDF(filePath, body);
});
First of all please be aware that doing this is really not a good idea; as explained in https://github.com/mozilla/pdf.js/wiki/Frequently-Asked-Questions#allthepages
How to do it;
Use the viewer provided by mozilla;
https://mozilla.github.io/pdf.js/web/viewer.html
modify BaseViewer class, _getVisiblePages() method in viewer.js to
/* load all pages */
_getVisiblePages() {
let visible = [];
let currentPage = this._pages[this._currentPageNumber - 1];
for (let i=0; i<this.pagesCount; i++){
let aPage = this._pages[i];
visible.push({ id: aPage.id, view: aPage, });
}
return { first: currentPage, last: currentPage, views: visible, };
}
If you want to render all pages of pdf document in different canvases
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="pdf.js"></script>
<script src="jquery.js"></script>
</head>
<body>
<h1>PDF.js 'Hello, world!' example</h1>
<div id="canvas_div"></div>
<body>
<script>
// If absolute URL from the remote server is provided, configure the CORS
// header on that server.
var url = 'pdff.pdf';
// Loaded via <script> tag, create shortcut to access PDF.js exports.
var pdfjsLib = window['pdfjs-dist/build/pdf'];
// The workerSrc property shall be specified.
pdfjsLib.GlobalWorkerOptions.workerSrc = 'worker.js';
var loadingTask = pdfjsLib.getDocument(url);
loadingTask.promise.then(function(pdf) {
var __TOTAL_PAGES = pdf.numPages;
// Fetch the first page
var pageNumber = 1;
for( let i=1; i<=__TOTAL_PAGES; i+=1){
var id ='the-canvas'+i;
$('#canvas_div').append("<div style='background-color:gray;text-align: center;padding:20px;' ><canvas calss='the-canvas' id='"+id+"'></canvas></div>");
var canvas = document.getElementById(id);
//var pageNumber = 1;
renderPage(canvas, pdf, pageNumber++, function pageRenderingComplete() {
if (pageNumber > pdf.numPages) {
return;
}
// Continue rendering of the next page
renderPage(canvas, pdf, pageNumber++, pageRenderingComplete);
});
}
});
function renderPage(canvas, pdf, pageNumber, callback) {
pdf.getPage(pageNumber).then(function(page) {
var scale = 1.5;
var viewport = page.getViewport({scale: scale});
var pageDisplayWidth = viewport.width;
var pageDisplayHeight = viewport.height;
//var pageDivHolder = document.createElement();
// Prepare canvas using PDF page dimensions
//var canvas = document.createElement(id);
var context = canvas.getContext('2d');
canvas.width = pageDisplayWidth;
canvas.height = pageDisplayHeight;
// pageDivHolder.appendChild(canvas);
// Render PDF page into canvas context
var renderContext = {
canvasContext: context,
viewport: viewport
};
page.render(renderContext).promise.then(callback);
});
}
</script>
<html>
The accepted answer works perfectly for a single PDF. In my case there were multiple PDFs that I wanted to render all pages for in the same sequence of the array.
I adjusted the code so that the global variables are encapsulated in an object array as follows:
var docs = []; // Add this object array
var urls = []; // You would need an array of the URLs to start.
// Loop through each url. You will also need the index for later.
urls.forEach((url, ix) => {
//Get the document from the url.
PDFJS.getDocument(url).then(function(pdf) {
// Make new doc object and set the properties of the new document
var doc = {};
//Set PDFJS global object (so we can easily access in our page functions
doc.thePDF = pdf;
//How many pages it has
doc.numPages = pdf.numPages;
//Push the new document to the global object array
docs.push(doc);
//Start with first page -- pass through the index for the handlePages method
pdf.getPage( 1 ).then(page => handlePages(page, ix) );
});
});
function handlePages(page, ix)
{
//This gives us the page's dimensions at full scale
var viewport = page.getViewport( {scale: 1} );
//We'll create a canvas for each page to draw it on
var canvas = document.createElement( "canvas" );
canvas.style.display = "block";
var context = canvas.getContext('2d');
canvas.height = viewport.viewBox[3];
canvas.width = viewport.viewBox[2];
//Draw it on the canvas
page.render({canvasContext: context, viewport: viewport});
//Add it to an element based on the index so each document is added to its own element
document.getElementById('doc-' + ix).appendChild( canvas );
//Move to next page using the correct doc object from the docs object array
docs[ix].currPage++;
if ( docs[ix].thePDF !== null && docs[ix].currPage <= docs[ix].numPages )
{
console.log("Rendering page " + docs[ix].currPage + " of document #" + ix);
docs[ix].thePDF.getPage( docs[ix].currPage ).then(newPage => handlePages(newPage, ix) );
}
}
Because the entire operation is asynchronous, without a unique object for each document, global variables of thePDF, currPage and numPages will be overwritten when subsequent PDFs are rendered, resulting in random pages being skipped, documents entirely skipped or pages from one document being appended to the wrong document.
One last point is that if this is being done offline or without using ES6 modules, the PDFJS.getDocument(url).then() method should change to pdfjsLib.getDocument(url).promise.then().
Make it to be iterate every page how much you want.
const url = '/storage/documents/reports/AR-2020-CCBI IND.pdf';
pdfjsLib.GlobalWorkerOptions.workerSrc = '/vendor/pdfjs-dist-2.12.313/package/build/pdf.worker.js';
const loadingTask = pdfjsLib.getDocument({
url: url,
verbosity: 0
});
(async () => {
const pdf = await loadingTask.promise;
let numPages = await pdf.numPages;
if (numPages > 10) {
numPages = 10;
}
for (let i = 1; i <= numPages; i++) {
let page = await pdf.getPage(i);
let scale = 1.5;
let viewport = page.getViewport({ scale });
let outputScale = window.devicePixelRatio || 1;
let canvas = document.createElement('canvas');
let context = canvas.getContext("2d");
canvas.width = Math.floor(viewport.width * outputScale);
canvas.height = Math.floor(viewport.height * outputScale);
canvas.style.width = Math.floor(viewport.width) + "px";
canvas.style.height = Math.floor(viewport.height) + "px";
document.getElementById('canvas-column').appendChild(canvas);
let transform = outputScale !== 1
? [outputScale, 0, 0, outputScale, 0, 0]
: null;
let renderContext = {
canvasContext: context,
transform,
viewport
};
page.render(renderContext);
}
})();

Categories