I have an app where images are drawn up on pages at screen width or less, but if I click them, I open a modal to show the image at full size. It works fine on ios but for some reason Android doesn't show the original image, but a much smaller sized image. I can't for the life of me figure out why it won't show the original image from url. I'm using a template for my modal:
<ScrollView #imgScroll orientation="horizontal">
<Image [src]="img" (loaded)="onImageLoaded($event);" (pinch)="onPinch($event)" #dragImage class="largeImage" stretch="none"></Image>
</ScrollView>
Then in my code, I'm setting the scroller and allowing the user to be able to drag the image around to inspect the entire image.
onImageLoaded(args: EventData){
let dragImage = <Image>args.object;
if(dragImage){
setTimeout(()=>{
this.imgSize = dragImage.getActualSize();
if(this.imgSize.width>this.imgSize.height){
this.orientation = "landscape";
this.imageScroller.scrollToHorizontalOffset(this.imgSize.width / 4, true);
} else {
this.orientation = "portrait";
this.imageScroller.scrollToVerticalOffset(this.imgSize.width / 4, true);
}
console.log("Image Size: ", this.imgSize);
},1500);
}
}
onImagePan(args: PanGestureEventData){
if (args.state === 1){
this.prevX = 0
this.prevY = 0;
}
if (args.state === 2) // panning
{
this.dragImageItem.translateX += args.deltaX - this.prevX;
this.dragImageItem.translateY += args.deltaY - this.prevY;
this.prevX = args.deltaX;
this.prevY = args.deltaY;
} else if (args.state === 3){
this.dragImageItem.animate({
translate: { x: 0, y: 0 },
duration: 1000,
curve: AnimationCurve.cubicBezier(0.1, 0.1, 0.1, 1)
});
}
}
onPinch(args: PinchGestureEventData) {
console.log("Pinch scale: " + args.scale + " state: " + args.state);
if(this.imgSize){
if (args.state === 1) {
var newOriginX = args.getFocusX() - this.dragImageItem.translateX;
var newOriginY = args.getFocusY() - this.dragImageItem.translateY;
}
}
}
Here is what gets logged to the console:
Image Size: {
"width": 533.3333333333334,
"height": 127.33333333333333
}
The actual image dimensions are 900 x 600.
It sort of appears Android is caching the image from the page and not showing the actual image from the url. I don't get why iOS works but Android does not. Anyone have any ideas?
Version: 1.17.0-v.2019.5.31.1 (latest)
NativeScript CLI version: 5.4.2
CLI extension nativescript-cloud version: 1.17.6
CLI extension nativescript-starter-kits version: 0.3.5
You are comparing device independent pixels with actual pixels.
getActualSize() returns width and height in DPI format, use utils.layout. toDevicePixels(getActualSize().width) Or utils.layout. toDevicePixels(getActualSize().height) to get actual pixel value.
Related
So I am building a photo group creator in Nativescript-vue. I have the pan working and the view is rendering correctly. But my issue is when a user starts to pan the image outside the container it's in, the image goes 'behind' the container. And I need it to render ontop of everything. I have tried 'z-index' with css and still no go.
The idea is, there is a group of photos (un-grouped group). And the user will be able to click-and-drag a photo in the group down to an 'empty' group. And this in turn would create a new group. Or if there were other groups the user would be able to drag an image and add it to an existing group.
Any help or suggestions, thank you in advance!
This is my Vue Component
<template>
<StackLayout orientation="vertical" ref="mainContainer">
</StackLayout>
</template>
<script>
import * as StackLayout from 'tns-core-modules/ui/layouts/stack-layout';
import * as Image from 'tns-core-modules/ui/image';
import _ from 'underscore';
export default {
name: "photo-groupings",
props:{
photoList:{
type: Object,
required: true
}
},
mounted(){
let ungrouped = this.createGroupElement();
let newGroupElement = this.createGroupElement();
console.log(_.keys(this.photoList));
for(let pht of _.keys(this.photoList)){
console.log(pht);
console.log(this.photoList[pht]);
ungrouped.addChild(this.createImageChild(this.photoList[pht]))
}
this.$refs.mainContainer.nativeView.addChild(ungrouped);
this.$refs.mainContainer.nativeView.addChild(newGroupElement)
},
data(){
return {
photoGroups:{},
groupElements:{},
prevDeltaX: 0,
prevDeltaY: 0
}
},
methods:{
createImageChild(src){
let tempImg = new Image.Image();
tempImg.src = src;
tempImg.width = 100;
tempImg.height = 100;
tempImg.stretch = "aspectFill";
tempImg.borderRadius = 10;
tempImg.borderWidth = 2;
tempImg.borderColor = "forestgreen";
tempImg.margin = 5;
tempImg.on('pan', this.handlePan);
return tempImg;
},
createGroupElement(){
let tempGroup = new StackLayout.StackLayout();
tempGroup.orientation = "horizontal";
tempGroup.margin = 5;
tempGroup.borderColor = "black";
tempGroup.borderRadius = 5;
tempGroup.borderWidth = 1;
tempGroup.minHeight = 110;
return tempGroup;
},
handlePan(args){
if (args.state === 1) // down
{
this.prevDeltaX = 0;
this.prevDeltaY = 0;
console.log(args.view.getLocationRelativeTo(args.view.parent));
console.log(args.view.parent.getLocationInWindow());
}
else if (args.state === 2) // panning
{
args.view.translateX += args.deltaX - this.prevDeltaX;
args.view.translateY += args.deltaY - this.prevDeltaY;
this.prevDeltaX = args.deltaX;
this.prevDeltaY = args.deltaY;
}
else if (args.state === 3) // up
{
console.log("Pan release")
}
}
}
}
</script>
<style scoped>
</style>
Example Image of the images rendering 'behind'
The best way here would be creating a ghost element.
You should not move the original image but hide the original image when you detect drag, create a ghost of original image and insert it on the parent layout. When user drops the ghost image, destroy the ghost and move original image to destination.
I am rendering OSM map tiles onto a web page using HTML canvas drawImage. However where an end user has selected dark mode, I would like to reduce the luminosity of these displayed maps, yet still allow them to make sense to the user.
So far I have had moderate success, as follows:
First plotting the map tile using drawImage
setting globalCompositeOperation to "difference"
over plotting the map tile with a white rectangle of the same size
setting globalCompositeOperation back to "source-over"
But this simple colour inversion is not perhaps the best solution. Does anyone have any other suggestions.
You could switch to a different tile server with a different map style. Check for example "CartoDB.DarkMatter" from Leaflet Provider Demo or MapBox Light & Dark.
I have found a pretty good solution to this and it is as follows:
First set the canvas context filter to "hue-rotate(180deg)"
Then plot the map tile on the canvas using drawImage
Then set the canvas context filter to "none"
The set canvas context globalCompositeOperation to "difference"
Then over plot the map tile with a white rectangle of the same size
Finally set canvas context globalCompositeOperation back to "source-over"
Maybe someone will still find this useful, it's some code i'm using for this purpose in my tar1090 project.
Negative and positive contrast are probably clear and dim is basically just a brightness modification with inverted sign.
toggle function:
function setDim(layer, state) {
if (state) {
layer.dimKey = layer.on('postrender', dim);
} else {
ol.Observable.unByKey(layer.dimKey);
}
OLMap.render();
}
postrender function:
function dim(evt) {
const dim = mapDimPercentage * (1 + 0.25 * toggles['darkerColors'].state);
const contrast = mapContrastPercentage * (1 + 0.1 * toggles['darkerColors'].state);
if (dim > 0.0001) {
evt.context.globalCompositeOperation = 'multiply';
evt.context.fillStyle = 'rgba(0,0,0,'+dim+')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
} else if (dim < -0.0001) {
evt.context.globalCompositeOperation = 'screen';
console.log(evt.context.globalCompositeOperation);
evt.context.fillStyle = 'rgba(255, 255, 255,'+(-dim)+')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
}
if (contrast > 0.0001) {
evt.context.globalCompositeOperation = 'overlay';
evt.context.fillStyle = 'rgba(0,0,0,'+contrast+')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
} else if (contrast < -0.0001) {
evt.context.globalCompositeOperation = 'overlay';
evt.context.fillStyle = 'rgba(255, 255, 255,'+ (-contrast)+')';
evt.context.fillRect(0, 0, evt.context.canvas.width, evt.context.canvas.height);
}
evt.context.globalCompositeOperation = 'source-over';
}
toggle function when using LayerSwitcher:
function setDimLayerSwitcher(state) {
if (!state) {
ol.control.LayerSwitcher.forEachRecursive(layers_group, function(lyr) {
if (lyr.get('type') != 'base')
return;
ol.Observable.unByKey(lyr.dimKey);
});
} else {
ol.control.LayerSwitcher.forEachRecursive(layers_group, function(lyr) {
if (lyr.get('type') != 'base')
return;
lyr.dimKey = lyr.on('postrender', dim);
});
}
OLMap.render();
}
I am currently creating a floormap using HTML and Canvas.
Here is the test url : http://test.fastcoding.jp/clem/floormap/
Map image for each zoom level is cut into pieces (with PHP), and map is rebuilt using HTML divs with different transform: translate() positions.
On mouse wheel events, a scale factor is applied to each divs, and once the scale become 1 the next zoom level is shown.
On mouse drag, translate values are updated.
So now here is the problem.
Everything works fine on Chrome, Safari, but on IE dragging actions are really laggy (a bit laggy in Firefox as well).
Here is my map refresh function that is called on drag :
app.refresh_map = function() {
var curr_map = app.maps[app.curr_zoom];
var maxI = curr_map.tiles.length-1;
var maxJ = curr_map.tiles[0].length-1;
var scale = app.zooms[app.curr_zoom][app.curr_scale];
for(var i=0; i<=maxI; i++) {
for(var j=0; j<=maxJ; j++) {
var curr_tile = curr_map.tiles[i][j];
var new_tile = {
x: curr_tile.x*scale, y: curr_tile.y*scale,
width: curr_tile.width, height: curr_tile.height
};
var selector = ".fm-tiles[data-map='"+app.mapID+"'] .fm-tile[data-zoom='"+app.curr_zoom+"'][data-tile='"+i+"-"+j+"']";
if ( app.in_screen(new_tile) ) {
if( $(selector).length==0 ) $tiles.append('<div class="fm-tile" data-zoom="'+app.curr_zoom+'" data-tile="'+i+"-"+j+'"></div>');
var $tile = $(selector);
var x = app.pos.x + new_tile.x;
var y = app.pos.y + new_tile.y;
x = parseFloat(x.toFixed(2));
y = parseFloat(y.toFixed(2));
if(!$tile.hasClass("loaded")) {
$tile.css("background", "url('api/results/"+mapID+"/"+app.curr_zoom+"/"+curr_tile.src+"') no-repeat left top");
$tile.css("background-size", "100% 100%");
$tile.addClass("loaded");
$tile.css({
"width": curr_tile.width+"px",
"height": curr_tile.height+"px"
});
}
if(app.browser=="ie") {
$tile.css({
"transform-origin": "0% 0%",
"-ms-transform": "translate("+x+"px,"+y+"px) scale("+scale+","+scale+")"
});
}
else {
$tile.css({
"transform-origin": "0px 0px 0px",
"-webkit-transform": "translate3d("+x+"px,"+y+"px, 0px) scale("+scale+","+scale+") rotate(0.01deg)",
"-moz-transform": "translate3d("+x+"px,"+y+"px, 0px) scale("+scale+","+scale+") rotate(0.01deg)",
"transform": "translate3d("+x+"px,"+y+"px, 0px) scale("+scale+","+scale+") rotate(0.01deg)"
});
}
if(!$tile.is(":visible")) {
$tile.show().addClass("shown");
}
}
else {
if($(selector).length!=0){
$(selector).hide();
}
}
}
}
$(".fm-tiles[data-map='"+app.mapID+"'] .fm-tile:not([data-zoom='"+app.curr_zoom+"'])").hide();
}
I know it is possible to make it smooth because there is this map which work perfectly on any browser : http://okayamaeki-sc.jp/floorguide/#/sunste2f
I looked a bit the code but it is ugglyfied so a bit hard to understand.
I know that it uses different transform and transform-origin according to the browser and that it is using a sort of easing, but I don't know if it's the reason why it is so smooth.
Does anyone have any clue ?
Thanks.
I'm using mediaelement.js version 2.16.4 to load video in an iframe created by thickbox.js. When I try to click fullscreen button in firefox-37.0.2/IE-11 the video goes to fullscreen but immediately goes back to smaller size of the iframe. If I don't initialize mediaelement and only the HTML5 tag then the fullscreen works fine in an iframe. So, it has to do something with the mediaelement player. This doesn't happen in Chrome! Can anybody guide me here why this is happening and how it can be resolved?
FYI: I have "webkitallowfullscreen mozallowfullscreen allowfullscreen" in the iframe.
<video id="videoPlayer" width="100%" height="100%" controls="controls" preload="none" autoplay="true">
<source type="video/mp4" src="<%=Url%>" />
</video>
$(document).ready(function() {
if ($('video')) {
var player = $('video').mediaelementplayer({
videoWidth: '100%',
videoHeight: '100%',
// initial volume when the player starts
startVolume: 0.8,
// enables Flash and Silverlight to resize to content size
//enableAutosize: true,
// the order of controls you want on the control bar (and other plugins below)
features: ['playpause', 'current', 'progress', 'duration', 'tracks', 'volume', 'fullscreen'],
// Hide controls when playing and mouse is not over the video
alwaysShowControls: false,
isNativeFullScreen: true,
success: function (mediaElement, domObject, player) {
mediaElement.addEventListener('ended', function () {
player.exitFullScreen();
}, false);
}
});
}
});
I found the issue of fullscreen in firefox/IE. It's the following code in mediaelement-and-player.js where there is a manual exit from fullscreen. For me the 'zoomMultiplier' variable is 1.25 which is causing the problem. I can comment this code but not sure if it breaks other scenario? -
if (t.isInIframe) {
// sometimes exiting from fullscreen doesn't work
// notably in Chrome <iframe>. Fixed in version 17
setTimeout(function checkFullscreen() {
if (t.isNativeFullScreen) {
var zoomMultiplier = window["devicePixelRatio"] || 1;
// Use a percent error margin since devicePixelRatio is a float and not exact.
var percentErrorMargin = 0.002; // 0.2%
var windowWidth = zoomMultiplier * $(window).width();
var screenWidth = screen.width;
var absDiff = Math.abs(screenWidth - windowWidth);
var marginError = screenWidth * percentErrorMargin;
// check if the video is suddenly not really fullscreen
if (absDiff > marginError) {
// manually exit
t.exitFullScreen();
} else {
// test again
setTimeout(checkFullscreen, 500);
}
}
}, 500);
}
I have an image object in QML (QtQuick 1.0), its height must be relative (in case of window scaling), but the problem is when I try to display this image in fullsize. Please look at the following part of my code:
Image {
id: selectionDialogImage
source: "qrc:/Images/myImage.png"
property int oldHeight: sourceSize.height
sourceSize.height: testsList.height/3
scale: 1
anchors.bottom: parent.bottom
anchors.left: parent.left
visible: false
property bool imageFullsize: false
MouseArea {
anchors.fill: parent
onClicked:
{
if(parent.imageFullsize)
{
parent.parent = selectionDialogQuestionText;
parent.sourceSize.width = undefined;
parent.sourceSize.height = testsList.height/3;
parent.anchors.horizontalCenter = undefined;
parent.anchors.verticalCenter = undefined;
parent.anchors.left = selectionDialogQuestionText.left;
parent.anchors.bottom = selectionDialogQuestionText.bottom;
parent.imageFullsize = false;
}
else
{
parent.parent = mainItem;
parent.sourceSize.height = parent.oldHeight;
//parent.sourceSize.height = undefined;
parent.sourceSize.width = mainItem.width;
parent.anchors.horizontalCenter = mainItem.horizontalCenter;
parent.imageFullsize = true;
}
}
}
}
selectionDialogQuestionText is a default parent of my image item. mainItem is the largest item, it has size of the whole window. So I want to have the image size set on the basis of width when I'm displaying it on a fullscreen, but in another state I want to scale the image setting its height.
When I set parent.sourceSize.width = mainItem.width;, image isn't scaled, but its height is as previous (very little) so its proportions are inappropriate.
Can I store old height of source image in any way? I need something like const, because property int oldHeight: sourceSize.heigh is relative. Or is there a way to restore default size of image and then set height/width?
UPDATE #1:
I tried to use method described by #Mitch:
property int oldHeight
Component.onCompleted: {
oldHeight = sourceSize.height
}
sourceSize.height: 250
but console.log("oldHeight="+parent.oldHeight) a few lines below shows oldHeight=250, not original height.
Can I store old height of source image in any way?
You can use Component.onCompleted:
Image {
// ...
Component.onCompleted: {
oldHeight = sourceSize.height
}
}