Why does my canvas not always display the drawn image? - ionic-framework

In my ionic app, I am trying to scale a photo and draw it onto a canvas. Here's my code:
HTML:
<ion-header>
<ion-toolbar>
<ion-icon name="camera" slot="start" style="padding-left: 8px; font-size: x-large"></ion-icon>
<ion-title>Photo</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<canvas id="mainCanvas" #mainCanvas style="display: block; padding-top: 20px; margin: 10px auto; height: 90%;"></canvas>
</ion-content>
<ion-footer>
<ion-item lines="none">
<ion-tabs tabsPlacement="bottom">
<ion-tab-bar slot="bottom">
<ion-tab-button size="small" (click)="openCamera()">
<ion-icon name="camera"></ion-icon>
<ion-label>Add Photo</ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</ion-item>
</ion-footer>
.ts:
private updateCanvas() {
const img = new Image();
const ctx = this._CONTEXT;
const canvas = this._CANVAS;
// set final size of image to be displayed (half the size of the screen, to allow for other controls to be shown)
const displayWidth = this.plt.width() / 1.60;
const displayHeight = this.plt.height() / 1.60;
canvas.width = displayWidth;
canvas.height = displayHeight;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = "#ff0000";
if (this.photo == null) {
console.log("updateCanvas: photo object is null");
return;
}
// Create an offscreen buffer to draw the full-sized photo onto
const bufferCanvas = document.createElement('canvas');
const bufferCtx = bufferCanvas.getContext('2d');
const path = this.file.dataDirectory + this.photo.id + "." + this.photo.file_extension;
img.onload = (() => {
console.log("updateCanvas > img.onload > photo: ", this.photo);
// Scale the buffer canvas to match original size of photo
bufferCanvas.width = img.width;
bufferCanvas.height = img.height;
if (bufferCtx && ctx) {
// Draw photo to buffer canvas
console.log("updateCanvas > drawing onto buffer...");
bufferCtx.drawImage(img, 0, 0);
// Get the photo and main canvas aspect ratios
const imgAspect = img.height / img.width;
const mainAspect = canvas.height / canvas.width;
// Should the image fill the canvas width, or height?
const fillWidth = true; // imgAspect > mainAspect;
// Determine the new scale of the image so that it covers the main canvas
// This would need adjusting if we wanted it contained within the main canvas (no overhang)
const scaledWidth = fillWidth ? displayWidth : displayHeight / imgAspect;
const scaledHeight = fillWidth ? displayWidth * imgAspect : displayHeight;
// Translate to the center of the main canvas
ctx.translate(displayWidth / 2, displayHeight / 2);
// Draw the buffer to the main canvas
// Take off half the scaled dimensions so that it doesn't draw in the bottom right corner
ctx.drawImage(bufferCanvas, -scaledWidth / 2, -scaledHeight / 2, scaledWidth, scaledHeight);
}
});
console.log("updateCanvas > path: ", path);
img.src = (<any>window).Ionic.WebView.convertFileSrc(path);
}
However, my updateCanvas method only seems to work randomly when called. It's completely random, sometimes it will draw after two calls, sometimes only after 7 calls. Can anyone spot what might be preventing the canvas from being drawn? I have verified that the photo file exists in the folder specified by the path variable. If I draw to the canvas without scaling, the image is drawn to the canvas without problems.
edit1: noticed something weird - when I display an alert before updating the canvas, the canvas shows the correctly scaled photo. E.g.
this.presentAlert("test", "test")
.then(() => this.updateCanvas());

Related

Using leaflet.FreeDraw with leaflet.path.drag

I am wondering if it's possible to use Leaflet.FreeDraw with leaflet.path.drag to be able to drag the polygon created by FreeDraw plugin.
jsfiddle
I tried to enable dragging plugin like this, but it doesn't work.
const map = new L.Map(document.querySelector('section.map'), { doubleClickZoom: false }).setView(LAT_LNG, 14);
L.tileLayer(TILE_URL).addTo(map);
const freeDraw = new FreeDraw({ mode: FreeDraw.ALL });
map.addLayer(freeDraw);
freeDraw.dragging.enable();
You could extract the bounds from the FreeDraw by listening to the markers event to create a polygon or other map object using leaflet enabled with dragging. See working example below.
You should consider whether you would like to disable the FreeDraw after this, using the option leaveModeAfterCreate:true as the user may get additional polygons when dragging
const LAT_LNG = [51.505, -0.09];
const TILE_URL = 'https://cartodb-basemaps-a.global.ssl.fastly.net/light_all/{z}/{x}/{y}#2x.png';
const map = new L.Map(document.querySelector('section.map'), { doubleClickZoom: false }).setView(LAT_LNG, 14);
L.tileLayer(TILE_URL).addTo(map);
const freeDraw = new FreeDraw({
mode: FreeDraw.ALL,
leaveModeAfterCreate:true //recommended to prevent undesired creation of multiple polygons
});
map.addLayer(freeDraw);
//freeDraw.dragging.enable();
//STEP 1: Listen to markers event raised by free draw whenever edits (create/edit/deletions are made to the map)
freeDraw.on("markers",function(event){
//we are only interested in create events
//we aim to extract the bounds and remove the existing
// freedraw references. If it is that you would like your
// user to edit the polygon, then you may keep these and do the // additional work to manage and update these references
if(event.eventType=='create' && event.latLngs.length > 0){
//capture the current polygon bounds (store in 1st position)
var latLngs = event.latLngs[0];
freeDraw.clear(); //clear freedraw markers
//create polygon from lat lng bounds retrieved
var polygon = L.polygon(
latLngs.map(function(latLng){
return [latLng.lat,latLng.lng];
}), {
color: 'red',
draggable: true //make polygon draggable
}).addTo(map);
}
})
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}
.map {
width: 100vw;
height: 100vh;
}
.map.mode-create {
cursor: crosshair;
}
.leaflet-edge {
background-color: #95bc59;
box-shadow: 0 0 0 2px white, 0 0 10px rgba(0, 0, 0, .35);
border-radius: 50%;
cursor: move;
outline: none;
transition: background-color .25s;
}
.leaflet-polygon {
fill: #b4cd8a;
stroke: #50622b;
stroke-width: 2;
fill-opacity: .75;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.2/leaflet.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.0.2/leaflet-src.js"></script>
<script src="https://rawgit.com/Wildhoney/Leaflet.FreeDraw/master/dist/leaflet-freedraw.iife.js"></script>
<script src="https://npmcdn.com/leaflet.path.drag/src/Path.Drag.js"></script>
<section class="map"></section>
NB. Also see working example on js-fiddle here https://jsfiddle.net/ytevLbgs/

Mapbox-gl-js - Mouse events are off after applying CSS transform to map

I am using mapbox-gl-js to display a map inside of a responsive container.
Regardless of the size of the responsive container, I want the map to maintain the same resolution such that resizing the window does not affect the bounds or zoom level of the map, it simply scales the map as if it were a static image.
I achieve this by dynamically calculating the scale factor and applying it to the container via CSS transform:
#map {
width: 100%;
height: 100%;
}
#map-scaler {
width: 1280px;
height: 1280px;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
transform-style: preserve-3d;
transform-origin: 0 0;
}
#map-container {
background-color: #fff;
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
transform-style: preserve-3d;
width: 100%;
height: 100%;
}
<div id="map-container">
<div id="map-scaler">
<div id="map"></div>
</div>
</div>
// Get size of map container for display
var mapContainer = $("#map-container");
var mcWidth = mapContainer.width();
var mcHeight = mapContainer.height();
// Get size of scaler container
var scaleContainer = $("#map-scaler");
var sWidth = scaleContainer.width();
var sHeight = scaleContainer.height();
var scaleWidth = mcWidth / sWidth;
var scaleHeight = mcHeight / sHeight;
$("#map-scaler").css("transform", "scale(" + scaleWidth + ", " + scaleHeight + ")");
This achieves the desired results visually. For example, I can have a 1280x1280 map displayed inside of a 500x500 container.
The problem is that all the mouse events are now "off". For example, if you attempt a map click or a scroll zoom, the map applies your mouse event to the wrong area of the map.
How can I compensate for my CSS transform so that mouse events accurately reflect where the user clicked?
Can you try the resize method? As mentionned in the doc, the method looks at your div width&height and resize your map. Event should be updated too.
https://www.mapbox.com/mapbox-gl-js/api/#Map#resize

Mobile Page Slider content with dynamic height

I am trying to implement the app that have 4 pages in 4 table. It supposed to be support swipe gesture, and each page contains dynamic data and make the pages have different height.
I tried to use responsive-slider-div-span.source example and integrate the 4 table into the slides.
I would like to set the width and height for the slides dynamically while the document ready, However, The slide will not show if the width and height is not pre set in the the slide div.
May I know any suggestion?
Typically, the size of jssor slider is defined in html code, and you can alter the size grammatically.
Given a slider at size 600x300, you can use javascript to change size before initialization of jssor slider.
<script>
jQuery(document).ready(function ($) {
var options = {};
//retrieve height of the slider, assuming the height should be 200px;
$("#slider1_container").height("200px");
$("#slider1_slides").height("200px");
var jssor_slider1 = new $JssorSlider$("slider1_container", options);
//According to your comment, please add following code
//Handle $EVT_PARK event BEGIN
function OnSliderPark(slideIndex, fromIndex) {
if (slideIndex == 0 || slideIndex == jssor_slider1.$SlidesCount() - 1) {
//do something scroll page to top
}
}
jssor_slider1.$On($JssorSlider$.$EVT_PARK, OnSliderPark);
//Handle $EVT_PARK event END
});
</script>
<div id="slider1_container" style="... width: 600px; height: 300px; ...">
<div u="slides" id="slider1_slides" style="... width: 600px; height: 300px; ...">
...
</div>
</div>

How does .#2x work [duplicate]

This question already has answers here:
Serving high res images to retina display
(3 answers)
Closed 9 years ago.
I am new at formatting my code to fit the iphone (especially 4 and 5). Do you add two images to the css (with no media query)? Or do you need a media query with that background image?
basically yes, you need to do the following:
.repeatingPattern {
background: url(../images/bgPattern.png) repeat;
background-size: 100px 100px;
}
#media only screen and (-webkit-min-device-pixel-ratio: 2) {
.repeatingPattern {
background: url(../images/bgPattern#2x.png) repeat;
}
}
but if you are using jquery it will be more efficient to do that like this:
<img class="hires" alt="" src="search.png" width="100" height="100" />
<script type="text/javascript">
$(function () {
if (window.devicePixelRatio == 2) {
var images = $("img.hires");
// loop through the images and make them hi-res
for(var i = 0; i < images.length; i++) {
// create new image name
var imageType = images[i].src.substr(-4);
var imageName = images[i].src.substr(0, images[i].src.length - 4);
imageName += "#2x" + imageType;
//rename image
images[i].src = imageName;
}
}
});
</script>

Change default photoset size on tumblr

I know tumblr has the defualt photoset sizes {photoset-500},{photoset-400},{photoset-250}, but I want to change the width, so that the width could be either 420, or 350. i have looked everywhere and cannot find a code to help me do this.
You can use {Photoset} which just resizes the photoset to fit the container (max size 700px).
https://www.tumblr.com/docs/en/custom_themes
You cannot change default sizes of images with Tumblr markup, but you can visually change size with CSS.
Tumblr markup:
{block:Photoset}
{block:Photos}
<img src="{PhotoURL-500}" class="photoset-img" />
{/block:Photos}
{/block:Photoset}
CSS:
.photoset-img { width: 420px; /* can be any value you want */ }
Add this script before the ending body tag. This will increase or decrease the size of the photoset
<script type="text/javascript">
//This will change the source address and display the correct size.
$(".photoset").each(function() {
var newSrc = $(this).attr("src").replace('700','860');
$(this).attr("src", newSrc);
});
//This will get the new size of the iframe and resize the iframe holder accordingly.
$(function(){
var iFrames = $('.photoset');
function iResize() {
for (var i = 0, j = iFrames.length; i < j; i++) {
iFrames[i].style.height = iFrames[i].contentWindow.document.body.offsetHeight + 'px';}
}
if ($.browser.safari || $.browser.opera) {
iFrames.load(function(){
setTimeout(iResize, 0);
});
for (var i = 0, j = iFrames.length; i < j; i++) {
var iSource = iFrames[i].src;
iFrames[i].src = '';
iFrames[i].src = iSource;
}
} else {
iFrames.load(function() {
this.style.height = this.contentWindow.document.body.offsetHeight + 'px';
});
}
});
</script>