[IMP] website_slides: add zoom controls, and set default zoom

- use a default scale when the viewer is shown, to fit the document to
the available space
- avoid using CSS scaling on the canvas, as this makes the output blurry
- add some zoom controls in the viewer
- fix CSS class for icon on the full-screen toggle (this toggle was
previously not displayed because of it)
- move download button left (to avoid overlapping with "Chat with Us"
box on odoo.com)

opw-2389716

closes odoo/odoo#62836

X-original-commit: 0247a94f252186aae33f5f137021fa04747fa2fb
Signed-off-by: Nicolas Lempereur (nle) <nle@odoo.com>
Signed-off-by: Nicolas Galler <ngaller@users.noreply.github.com>
This commit is contained in:
Nicolas Galler
2020-12-03 14:40:43 +00:00
parent 080dfb3d1d
commit 9e574ebc07
3 changed files with 73 additions and 13 deletions
@@ -8,14 +8,13 @@
// !!!!!!!!! use window.pdfjsLib and not pdfjsLib
var PDFSlidesViewer = (function(){
function PDFSlidesViewer(pdf_url, $canvas, disableWorker){
// pdf variables
this.pdf = null;
this.pdf_url = pdf_url || false;
this.pdf_scale = 1.5;
this.pdf_page_total = 0;
this.pdf_page_current = 1; // default is the first page
this.pdf_zoom = 1; // 1 = scale to fit to available space
// promise business
this.pageRendering = false;
this.pageNumPending = null;
@@ -59,7 +58,10 @@ var PDFSlidesViewer = (function(){
this.pageRendering = true;
return this.pdf.getPage(page_number).then(function(page) {
// Each PDF page has its own viewport which defines the size in pixels and initial rotation.
var viewport = page.getViewport(self.pdf_scale);
// We provide the scale at which to render it (relative to the natural size of the document)
var scale = self.getScaleToFit(page) * self.pdf_zoom;
var viewport = page.getViewport({ scale: scale });
// important to match, otherwise the browser will scale the rendered output and it will be ugly
self.canvas.height = viewport.height;
self.canvas.width = viewport.width;
// Render PDF page into canvas context
@@ -71,6 +73,10 @@ var PDFSlidesViewer = (function(){
// Wait for rendering to finish
return renderTask.promise.then(function () {
self.pageRendering = false;
if (self.pdf_zoom === 1 && scale > self.getScaleToFit(page)) {
// if the scale has changed (because we just added scrollbars) and we no longer fit the space
return self.renderPage(page_number);
}
if (self.pageNumPending !== null) {
// New page rendering is pending
self.renderPage(self.pageNumPending);
@@ -117,6 +123,17 @@ var PDFSlidesViewer = (function(){
return this.queueRenderPage(this.pdf_page_current);
};
/*
* Calculate a scale to fit the document on the available space.
*/
PDFSlidesViewer.prototype.getScaleToFit = function(page) {
var maxWidth = this.canvas.parentNode.clientWidth;
var maxHeight = this.canvas.parentNode.clientHeight;
var hScale = maxWidth / page.view[2];
var vScale = maxHeight / page.view[3];
return Math.min(hScale, vScale);
};
/**
* Displays the given page.
*/
@@ -7,6 +7,7 @@
$(function () {
if ($('#PDFViewer') && $('#PDFViewerCanvas')) { // check if presentation only
var MIN_ZOOM=1, MAX_ZOOM=10, ZOOM_INCREMENT=.5;
// define embedded viewer (minimal object of the website.slide.PDFViewer widget)
var EmbeddedViewer = function ($viewer) {
@@ -46,9 +47,12 @@ $(function () {
this.navUpdate(pageNumber);
}
},
on_resize: function() {
this.render_page(this.pdf_viewer.pdf_page_current);
},
// page switching
render_page: function (pageNumber) {
this.pdf_viewer.renderPage(pageNumber).then(this.on_rendered_page.bind(this));
this.pdf_viewer.queueRenderPage(pageNumber).then(this.on_rendered_page.bind(this));
this.navUpdate(pageNumber);
},
change_page: function () {
@@ -96,10 +100,24 @@ $(function () {
self.$("#slide_suggest").addClass('d-none');
});
},
zoomIn: function() {
if(this.pdf_viewer.pdf_zoom < MAX_ZOOM) {
this.pdf_viewer.pdf_zoom += ZOOM_INCREMENT;
this.render_page(this.pdf_viewer.pdf_page_current);
}
},
zoomOut: function() {
if(this.pdf_viewer.pdf_zoom > MIN_ZOOM) {
this.pdf_viewer.pdf_zoom -= ZOOM_INCREMENT;
this.render_page(this.pdf_viewer.pdf_page_current);
}
},
navUpdate: function (pageNum) {
this.$('#first').toggleClass('disabled', pageNum < 3 );
this.$('#previous').toggleClass('disabled', pageNum < 2 );
this.$('#next, #last').removeClass('disabled');
this.$('#zoomout').toggleClass('disabled', this.pdf_viewer.pdf_zoom <= MIN_ZOOM);
this.$('#zoomin').toggleClass('disabled', this.pdf_viewer.pdf_zoom >= MAX_ZOOM);
},
// full screen mode
fullscreen: function () {
@@ -133,6 +151,12 @@ $(function () {
$('#last').on('click', function () {
embeddedViewer.last();
});
$('#zoomin').on('click', function () {
embeddedViewer.zoomIn();
});
$('#zoomout').on('click', function () {
embeddedViewer.zoomOut();
});
$('#page_number').on('change', function () {
embeddedViewer.change_page();
});
@@ -142,6 +166,19 @@ $(function () {
$('#PDFViewer').on('click', function (ev) {
embeddedViewer.fullScreenFooter(ev);
});
$('#PDFViewer').on('wheel', function (ev) {
if (ev.metaKey || ev.ctrlKey) {
if (ev.originalEvent.deltaY > 0) {
embeddedViewer.zoomOut();
} else if(ev.originalEvent.deltaY < 0) {
embeddedViewer.zoomIn();
}
return false;
}
});
$(window).on('resize', _.debounce(function() {
embeddedViewer.on_resize();
}, 500));
// switching slide with keyboard
$(document).keydown(function (ev) {
@@ -77,8 +77,8 @@
</div>
</div>
<t t-if="slide.slide_type in ('presentation', 'document')">
<div id="PDFViewerLoader" class="oe_slides_loader position-absolute mt-3 mr-3" style="right:0">
<div class="toast show">
<div id="PDFViewerLoader" class="oe_slides_loader mt-3 mx-2 w-100">
<div class="toast show mx-auto">
<div class="toast-header">
<i class="fa fa-circle-o-notch fa-spin mr-2"/><b>Loading...</b>
</div>
@@ -87,7 +87,7 @@
</div>
</div>
</div>
<canvas id="PDFViewerCanvas" class="img-fluid w-100" style="display: none;"></canvas>
<canvas id="PDFViewerCanvas" class="mx-auto" style="display: none;"></canvas>
</t>
<t t-if="slide.slide_type == 'infographic'">
<img t-att-src="website.image_url(slide, 'image_1024')" class="img-fluid" style="width: 100%" alt="Slide image"/>
@@ -97,28 +97,34 @@
<div id="PDFViewerNav" class="pt-2 pb-2 bg-light text-white" role="navigation" t-if="slide.slide_type in ('presentation', 'document')">
<div class="container-fluid oe_slides_panel_footer">
<div class="row align-items-center">
<div class="col-2 flex-grow-0">
<div class="col-3 d-flex align-items-center">
<div class="input-group input-group-sm flex-nowrap" style="max-width: 100px">
<input type="number" class="form-control text-center" id="page_number" style="min-width: 60px"/>
<div class="input-group-append">
<span class="input-group-text" id="page_count"/>
</div>
</div>
<a id="zoomout" href="#" class="text-decoration-none d-none d-sm-inline ml-2 mr-2" title="Zoom out" aria-label="Zoom out" role="button">
<i class="fa fa-search-minus" />
</a>
<a id="zoomin" href="#" class="text-decoration-none d-none d-sm-inline" title="Zoom in" aria-label="Zoom in" role="button">
<i class="fa fa-search-plus" />
</a>
</div>
<div class="col text-center">
<a id="first" href="#" class="text-decoration-none mr-1 mr-sm-2" title="First slide" role="button" aria-label="First slide"> <i class="fa fa-step-backward"/> </a>
<a id="previous" href="#" class="text-decoration-none mx-1 mx-sm-2" title="Previous slide" aria-label="Previous slide" role="button"> <i class="fa fa-arrow-circle-left"/> </a>
<a id="next" href="#" class="text-decoration-none mx-1 mx-sm-2" title="Next slide" aria-label="Next slide" role="button"> <i class="fa fa-arrow-circle-right"/> </a>
<a id="last" href="#" class="text-decoration-none ml-1 ml-sm-2" title="Last slide" aria-label="Last slide" role="button"> <i class="fa fa-step-forward"/> </a>
</div>
<div class="col-2 flex-grow-0 text-right">
<a id="last" href="#" class="text-decoration-none mx-1 mx-sm-2" title="Last slide" aria-label="Last slide" role="button"> <i class="fa fa-step-forward"/> </a>
<a t-if="slide.slide_resource_downloadable" id="download" t-attf-href="/web/content/slide.slide/#{slide.id}/datas?download=true"
class="text-decoration-none mr-1 mr-sm-2" title="Download Content" role="img" aria-label="Download">
class="text-decoration-none ml-1 ml-sm-2" title="Download Content" role="img" aria-label="Download">
<i class="fa fa-download" />
</a>
</div>
<div class="col-3 text-right flex-grow-0">
<a id="fullscreen" href="#" class="text-decoration-none ml-1 ml-sm-2"
title="View fullscreen" role="img" aria-label="Fullscreen">
<i class="fas fa-expand-arrows-alt"/>
<i class="fa fa-arrows-alt"/>
</a>
</div>
</div>