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.
Related
UPDATE balloon panels are staying attached in below code. We still have an issue where when we close a balloon panel, and then scroll afterward, the balloon panel that was just closed reappears. Here’s the updated code.
HERE WAS THE ORIGINAL QUESTION I am trying to get the ckeditor balloonpanel to stay attached to the element it was initially attached to; currently, when I scroll in the editor, the balloonpanels do not stay in place. The problem is that the balloonpanels shift when the user scrolls in the editor -- they do not remain attached to their initial element they were attached to when I scroll in the editor. Here is the code for the ckeditor plugin. It creates a balloonpanel in a for loop on return of an web service Ajax call and stores the panel in a global array called panels :
( function() {
var arr = new Array();
var panels = [];
var saveCmd = {
readOnly: 1,
modes: { wysiwyg: 1,source: 1 },
exec: function( editor ) {
if ( editor.fire( 'grade_level_score' ) ) {
var $form = editor.element.$.form;
/**
* Destroys the balloon panel by removing it from DOM and purging
* all associated event listeners.
*/
// https://github.com/ckeditor/ckeditor-dev/blob/64749bb245d1e91f6a4ac4e97c9648ec47acda91/plugins/balloonpanel/plugin.js#L743-L745
var panel;
while ( ( panel = panels.pop() ) ) {
panel.destroy();
}
arr = []; // clear the array of user-editable areas
panels = []; // clear the array of panels
// https://stackoverflow.com/a/48022658
var ele = $(editor.editable().$);
var elementOfClass;
var i = 1;
// class "ice-ice-editable" is in a span
$('span',ele).each(function(){
// https://stackoverflow.com/a/35866999
var iceIceEditableClass = "ice-ice-editable";
var hasClassIceIceEditable = $(this).hasClass(iceIceEditableClass);
if( hasClassIceIceEditable ) {
console.log($(this).text());
console.log($(this).attr('class'));
console.log($(this).attr('id'));
var userEditable = "user-editable-" + i;
// If the specified attribute already exists, only the value is set/changed.
this.setAttribute("id","user-editable-" + i);
var record1 = { id : userEditable , userEditableArea : $(this).text() };
arr.push(record1);
i++;
}
});
var gradeLevelObject = new Object();
gradeLevelObject.textAreas = arr;
// var responseGradeLevelScoreWS = gradeLevelScore(gradeLevelObject);
// BEGIN for testing
var result = '{"textAreas":[{"id":"user-editable-1","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.] ","score":22.24,"readingGrade":7,"issues":["asdf","zxcv"]},{"id":"user-editable-2","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.] ","score":22.24,"readingGrade":0,"issues":[]},{"id":"user-editable-3","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.] ","score":22.24,"readingGrade":0,"issues":[]},{"id":"user-editable-4","userEditableArea":"[Insert information specific to what is being addressed (a brief description of request(s) and/or concern(s). Specific training resource document for letter writing assistance will be referenced here.] ","score":22.24,"readingGrade":0,"issues":[]}]}';
var responseGradeLevelScoreWS = JSON.parse(result);
// END for testing
console.log(responseGradeLevelScoreWS);
var i;
for (i = 0; i < responseGradeLevelScoreWS.textAreas.length; i++){
if ( responseGradeLevelScoreWS.textAreas[i].readingGrade > 6 ) {
var j;
var issues = '';
for (j = 0; j < responseGradeLevelScoreWS.textAreas[i].issues.length; j++) {
issues += '<p>' + responseGradeLevelScoreWS.textAreas[i].issues[j] + '</p>';
}
panel = new CKEDITOR.ui.balloonPanel( editor, {
title: 'Grade: ' + responseGradeLevelScoreWS.textAreas[i].readingGrade + '. Score: ' + responseGradeLevelScoreWS.textAreas[i].score,
content: ( (typeof issues === 'undefined' || issues == null) ? 'There are no suggestions in order to descrease the grade level score' : issues ),
width: 500,
height: 120
});
var element = editor.document.getById(responseGradeLevelScoreWS.textAreas[i].id);
panel.attach( element );
panel.registerFocusable(element);
panels.push( panel );
issues = '';
}
}
// We'll use throttling for scroll listener to reduce performance impact.
var scrollListener = CKEDITOR.tools.eventsBuffer( 100, function() {
for (i = 0; i < panels.length; i++) {
panels[i].attach( editor.document.getById( responseGradeLevelScoreWS.textAreas[i].id ), {
focusElement: false,
show: false
} );
}
} );
editor.window.on( 'scroll', scrollListener.input );
if ( $form ) {
try {
//$form.submit();
} catch ( e ) {
// If there's a button named "submit" then the form.submit
// function is masked and can't be called in IE/FF, so we
// call the click() method of that button.
if ( $form.submit.click )
$form.submit.click();
}
}
}
}
};
var pluginName = 'grade_level_score';
// Register a plugin named "save".
CKEDITOR.plugins.add( pluginName, {
// jscs:disable maximumLineLength
lang: 'en,en-au,en-ca,en-gb,es,es-mx', // %REMOVE_LINE_CORE%
// jscs:enable maximumLineLength
icons: 'grade_level_score', // %REMOVE_LINE_CORE%
hidpi: true, // %REMOVE_LINE_CORE%
init: function( editor ) {
// Save plugin is for replace mode only.
if ( editor.elementMode != CKEDITOR.ELEMENT_MODE_REPLACE )
return;
var command = editor.addCommand( pluginName, saveCmd );
command.startDisabled = !( editor.element.$.form );
editor.ui.addButton && editor.ui.addButton( 'Grade_Level_Score', {
//label: editor.lang.save.toolbar,
label: "Grade Level Score",
command: pluginName,
toolbar: 'custom,100'
} );
}
} );
} )();
Only Balloon Toolbar has built-in functionality for automatic reposition on scroll. Balloon Panel itself is a static element. However, it can be easily achieved by attaching scroll listener and repositioning visible panels on scroll:
// We'll use throttling for scroll listener to reduce performance impact.
var scrollListener = CKEDITOR.tools.eventsBuffer( 100, function() {
for (i = 0; i < panels.length; i++) {
panels[i].attach( editor.document.getById(ids[i]), {
focusElement: false,
show: false
} );
}
} );
editor.window.on( 'scroll', scrollListener.input );
See this codepen for the full code (reusing some parts of your original code).
I’m trying to get a NativeScript <ListView> to be transparent on iOS and I’m failing. I found an old thread on the topic at https://groups.google.com/forum/#!topic/nativescript/-MIWcQo-l6k, but when I try the solution it doesn’t work for me. Here’s my complete code:
/* app.css */
Page { background-color: black; }
<!-- main-page.xml -->
<Page xmlns="http://schemas.nativescript.org/tns.xsd" loaded="loaded">
<ListView id="list-view" items="{{ items }}" itemLoading="itemLoading">
<ListView.itemTemplate>
<Label text="{{ name }}" />
</ListView.itemTemplate>
</ListView>
</Page>
// main-page.js
var ios = require("utils/utils");
var Observable = require("data/observable").Observable;
var ObservableArray = require("data/observable-array").ObservableArray;
var page;
var items = new ObservableArray([]);
var pageData = new Observable();
exports.loaded = function(args) {
page = args.object;
page.bindingContext = pageData;
// Toss a few numbers in the list for testing
items.push({ name: "1" });
items.push({ name: "2" });
items.push({ name: "3" });
pageData.set("items", items);
};
exports.itemLoading = function(args) {
var cell = args.ios;
if (cell) {
// Use ios.getter for iOS 9/10 API compatibility
cell.backgroundColor = ios.getter(UIColor.clearColor);
}
}
Any help would be appreciated. Thanks!
Don't forget to set the listview to transparent, seems to have a backgroundcolor itself
ListView{
background-color: transparent;
}
Currently with NativeScript 2.4 the following works
var cell = args.ios;
if (cell) {
cell.selectionStyle = UITableViewCellSelectionStyleNone
}
And if you want to change the selection highlight color here is a simple approach, I have not tested performance but it works okay on an iPhone 6.
import { Color } from 'color';
cell.selectedBackgroundView = UIView.alloc().initWithFrame(CGRectMake(0, 0, 0, 0));
let blue = new Color('#3489db');
cell.selectedBackgroundView.backgroundColor = blue.ios
Not sure if there are better ways to do this, but this is what worked for me with NativeScript 2.4 on iOS to both A) make the ListView background transparent, and B) change the color when an item is tapped:
let lvItemLoading = (args) => {
let cell = args.ios;
if (cell) {
// Make the iOS listview background transparent
cell.backgroundColor = ios.getter(cell, UIColor.clearColor);
// Create new background view for selected state
let bgSelectedView = UIView.alloc().init();
bgSelectedView.backgroundColor = new Color("#777777").ios;
bgSelectedView.layer.masksToBounds = true;
cell.selectedBackgroundView = bgSelectedView;
}
};
I am working on a project for my programming class. I'd like to essentially have a canvas with a background element (say a room.jpg) and then maybe three interactive objects in the room (lamp.jpg, couch.jpg, desk.jpg). I'd like for it to be that if you hover over the lamp a small box or text pops out, giving you some information. Or maybe have it so if you click an image, the same concept happens. You know, something interactive with the objects in the canvas. Again, I'm new to canvas but we have to use it in our assignment. My current code is:
function loadImages(sources, callback) {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages++;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
var sources = {
room: 'room.jpg',
title: 'title.jpg'
};
loadImages(sources, function(images) {
context.drawImage(images.room, 0,0);
context.drawImage(images.title, 0,0);
});
}
But from what I understand, it makes the two jpegs a "permanent" canvas element (unable to be messed with). I had been trying to get it so that when I clicked I'd go from the title.jpg to the room.jpg but I've since given up. Essentially, all I want now is just to have the room.jpg appear when the page is first loaded, and have three other png objects on top (as objects in the room). Are these able to be interacted with, or do I have to put the images into the canvas in another way? Thanks for all your help and your patience!
// --- Image Loader ----
var images = {};
var loadedImages = 0;
var pictures = {
room: 'room.jpg',
title: 'title.jpg'
lamp1: 'lampoff.jpg'
lamp2: 'lampon.jpg'
};
function loadImages(sources, callback) {
var numImages = 0;
for(var src in sources)numImages++;
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if(++loadedImages >= numImages) {
callback(images);
}
};
images[src].src = sources[src];
}
}
// --- Mouse Down Functionality ----
$('#canvas').addEventListener('mouseDown', function(e){
if(e.clientX){
var rect = this.getBoundingClientRect();
if(rect) clickCanvas(e.clientX - rect.left, e.clientY - rect.top)
else clickCanvas(e.clientX - this.offsetLeft, e.clientY - this.offsetTop);
}else if(e.offsetX) clickCanvas(e.offsetX, e.offsetY);
else if(e.layerX) clickCanvas(e.layerX, e.layerY);
else console.warn("Couldn't Determine Mouse Coordinates");
})
var lampOn;
function drawCanvas(showLamp){
lampOn = showLamp;
canvas.width = canvas.width //clears canvas
context.drawImage(images.room, 0,0);
context.drawImage(images.title, 0,0);
if(lampOn){
context.drawImage(images.lamp2, 100,100);
}else{
context.drawImage(images.lamp1, 100,100);
}
}
function clickCanvas(x,y){
console.log('clicked canvas at:',x,y)
if(clickedLamp){
drawCanvas(!lampOn)
}
}
loadImages(pictures, function(images) {
drawCanvas(false)
});
Make sure to replace "clickedLamp" and "#canvas"! The idea here is that you redraw the same canvas, using the same function. Everytime you modify any of it, you rerender ALL of it. See if you can get this example working, it will help clarify alot. If you don't understand something comment
I'm trying to change the bitmap image in createjs and want to remove all children in a container when reset button is clicked. But removeAllChildren is not working in me.
function drawPhoneImage() {
stage = new createjs.Stage('canvas');
container = new createjs.Container();
phone = new createjs.Bitmap(phoneImg);
phone.x = 268;
phone.y = 64;
stage.addChild(container);
container.addChild(phone);
stage.update();
phone.addEventListener("click", function() {
console.log('phone clicked');
createjs.Ticker.addEventListener("tick", movePhoneImage);
});
}
function movePhoneImage(event) {
phone.x -=10;
if(phone.x < 156) {
phone.x =156;
showPhoneSnap();
}
stage.update(event);
}
Then after clicking the phone object, I'll need to replace it with another bitmap(which works):
function showPhoneSnap() {
snap = new createjs.Bitmap(snapImg);
snap.x = 156;
snap.y = 64;
container.removeAllChildren();
container.addChild(snap);
stage.update();
}
At first, removeAllChildren is working in the first child of the container, but when i tried resetting the stage after adding another bitmap in the container..removeAllChildren() is not working.
function resetStage() {
container.removeAllChildren();
stage.update();
}
I'm having a hard time solving this issue, thanks for anyone who can help.
Make sure that "snapImg" is an image that is loaded.
snap = new createjs.Bitmap(snapImg);
The issue is that you are not updating the stage when the image is loaded.
var image = new Image();
image.src = "path";
image.onload = showPhoneSnap;
function showPhoneSnap() {
//This will ensure that the image is ready.
var bmp = new createjs.Bitmap(this);
...
stage.update();
}
Is there an easy way to add a simple border to an image?
I'm loading in image thumbs, and would like to add a border at runtime, instead of having to edit all the thumbs.
I'm using Spark Image.
Thanks!
EDIT:
I need to add a 1 px white border around these thumbs. I set the size of the tumbs to be 90x90, to make them fit if they are either horisontal or vertical, but the actual images in my example scales down to 90x51 (this is not fixed, only 90x90 as a maximum is fixed)
This is my code for adding thumbNails to a TileGroup (loading the gallery from an xml file):
private function loadPopUpThumbs():void{
if(curThumbImg <= totThumbImg){
var thumbImg:Image = new Image();
var _loader:Loader = new Loader();
var imageNr:int = curThumbImg;
var thumbContainer:BorderContainer = new BorderContainer();
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE,function(e:Event):void{
thumbImg.source = e.currentTarget.content;
popUpImgGroup.addElement(thumbImg);
thumbImg.width = 90;
thumbImg.height = 90;
thumbImg.scaleMode = "letterbox";
thumbImg.verticalAlign = "bottom";
thumbImg.smooth = true;
thumbImg.id = "thumbImg" + imageNr;
//thumbImg.drawRoundRect(0,0,thumbImg.width,thumbImg.height, null, 0xffffff);
thumbImg.addEventListener(MouseEvent.CLICK, function(evt:MouseEvent):void{
popUpThumbClicked(imageNr.toString());
});
trace("Thumb added: " + popUpXMLList.(attribute('nr')==imageNr.toString()).#thumbURL);
curThumbImg++;
loadPopUpThumbs();
});
_loader.load(new URLRequest(encodeURI(popUpXMLList.(attribute('nr')==imageNr.toString()).#thumbURL)));
}else{
trace("DONE Adding thubs!!!");
}
}
Also: Would it be possible to add a linebreak in the items added to the TileGroup?
In my XML file I've defined a group attribute, so that I'm able to devide the images into groups. If I click a image from one group, I can skip next/prev within that group, but not to the next group. Is there any way for me to insert a linebreak to my TileGroup, so that I can listen for when the prevGroup != curGroup, and then add in some sort of spacing before continuing adding the next thumbs? All I need is a way to skip a line in the tileGroup :)
Thanks!
You can create new custom Image Class, extends spark Image. Very simple and clean. And set border size and color with css. See example:
package classes
{
import flash.display.CapsStyle;
import flash.display.JointStyle;
import flash.display.LineScaleMode;
import spark.components.Image;
public class ImageBorder extends Image
{
public function ImageBorder()
{
super();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
if (imageDisplay && imageDisplay.bitmapData)
{
var borderSize:Number = getStyle("borderSize") || 0;
var borderColor:Number = getStyle("borderColor") || 0xffffff;
var half:Number = borderSize/2;
imageDisplay.left = imageDisplay.top = imageDisplay.right = imageDisplay.bottom = borderSize;
graphics.clear();
graphics.lineStyle(borderSize, borderColor, 1, false, LineScaleMode.NONE, CapsStyle.NONE, JointStyle.MITER);
graphics.moveTo(0, half);
graphics.lineTo(unscaledWidth -half, half);
graphics.lineTo(unscaledWidth - half, unscaledHeight-half);
graphics.lineTo(half, unscaledHeight-half);
graphics.lineTo(half, half);
}
}
}
}
In application use with css:
<fx:Style>
#namespace s "library://ns.adobe.com/flex/spark";
#namespace mx "library://ns.adobe.com/flex/mx";
#namespace classes "classes.*";
classes|ImageBorder
{
borderSize : 10;
borderColor : "0xff00ff";
}
</fx:Style>
<classes:ImageBorder source=" your source url " />
Spark image is a SkinnableComponent.
You can create your custom skin that supports borders in any
convenient way, like styles or properties.
Or you can set that
skin if you want to see border or remove it if you don't want
Or you can put it inside BorderContainer, and set borderVisible to
true when you want to see the border.