Why doesn't ng-style work initially with a css transition? - angularjs

I'm trying to create a zoom effect, by adding a class and a width or height style to a span.
<span ng-class="fat_or_tall(this)"><figure><img ng-style="set_ratio(this)" draggable="true" class="thumb" id="{{tablet.created}}" title="{{ tablet.title }}" data-ng-src="{{ tablet.src }}" /></figure></span>
It works on the first page i'm on after the refresh, but when I click on the other tabs it just hops between sizes as if the css transition doesn't exist. If I inspect I see that sometime the styles have rendered, but sometimes they have not. After I click around the tabs and visit each one, then the zoom works properly. Why doesn't it work on the first try, and how can I fix that?
First image above notice there is no style, and the transition does not work, but after I click the tab a second time (second pic) the style does show and the transition works well.
HERE: http://jsfiddle.net/U3pVM/23506/ (If you click refresh it won't wont, but if you click run it will. The style won't render. WHY?)
function TodoCtrl($scope) {
$scope.tablet = {}
$scope.tablet.title = 'help'
$scope.tablet.created = Date.now()
$scope.tablet.src = 'http://tallclub.co.uk/wp-content/uploads/2011/04/Tall-Person-1.png'
$scope.set_ratio = function (it) { //Find ratio and return style so that it can be used for zoom effect.
console.log(it)
var img = new Image();
img.src = it.tablet.src;
console.log(it.tablet)
if(img.height>img.width){
var h = 300*(img.height/img.width)+'px'
return {
"height": h
}
}else{
var w = 300*(img.width/img.height)+'px'
return {
"width":w
}
}
}
}
function TodoCtrlTwo($scope) {
$scope.tablet = {}
$scope.tablet.title = 'help'
$scope.tablet.created = Date.now()
$scope.tablet.src = 'http://tallclub.co.uk/wp-content/uploads/2011/04/Tall-Person-1.png'
$scope.set_ratio = function (it) { //Find ratio and return style so that it can be used for zoom effect.
console.log(it)
var img = new Image();
img.src = it.tablet.src;
console.log(it.tablet)
if(img.height>img.width){
var h = 300*(img.height/img.width)+'px'
return {
"height": h
}
}else{
var w = 300*(img.width/img.height)+'px'
return {
"width":w
}
}
}
}
.taller img {
-webkit-transition: .3s ease-in-out;
transition: .3s ease-in-out;
}
.taller img:hover {
-webkit-transition: .3s ease-in-out;
transition: .3s ease-in-out;
height: 300px !important;
}
figure {
width: 300px;
height: 300px;
//height: 200px;
margin: 1em;
padding: 0;
background: #fff;
overflow: hidden;
border: 1px solid #222;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app>
<h2>ARGH</h2>
<div ng-controller="TodoCtrl">
<span class="taller"><figure><img ng-style="set_ratio(this)" draggable="true" class="thumb" id="{{tablet.created}}" title="{{ tablet.title }}" data-ng-src={{tablet.src}} /></figure></span>
</div>
<div ng-controller="TodoCtrlTwo">
<span class="taller"><figure><img ng-style="set_ratio(this)" draggable="true" class="thumb" id="{{tablet.created}}" title="{{ tablet.title }}" data-ng-src={{tablet.src}} /></figure></span>
</div>
</div>
Refresh page and click run code snippet, and mouse over. The images will jump. Then click run code snippet again and mouse over. There will be a smooth transition. Why is that?

Solution 1:
Working Demo
On first load of image, both width and height of the image are 0. So code of else block get executed which returns NaNpx.
In detail:
var w = 300*(img.width/img.height)+'px'
var w = 300*(0/0)+'px' // As img.width = 0, img.height = 0
var w = 300*(NaN)+'px'
var w = NaNpx
To resolve this, set initial height of the image:
.taller img {
-webkit-transition: .3s ease-in-out;
transition: .3s ease-in-out;
height: 500px;
}
Solution 2:
You can add following if block in both controller.
if(img.height===0 && img.width ===0){
var h = '500px';
return {
"height": h
}
}
Working Demo
Hope that solve your problem.

try this. create $scope.tablet ={}; object first.and change ng-class to class for span tag.
var app = angular.module("app",[])
app.controller('ctrl',['$scope', function($scope){
$scope.tablet ={};
$scope.tablet.title = 'help';
$scope.tablet.created = Date.now();;
$scope.tablet.src = 'http://tallclub.co.uk/wp-content/uploads/2011/04/Tall-Person-1.png'
$scope.set_ratio = function (it) { //Find ratio and return style so that it can be used for zoom effect.
console.log(it);
var img = new Image();
img.src = it.tablet.src;
console.log(it.tablet)
if(img.height>img.width){
var h = 300*(img.height/img.width)+'px'
return {'height':h};
}else{
var w = 300*(img.width/img.height)+'px'
return {
'width':w
}
}
}
}]);
.taller img {
-webkit-transition: .3s ease-in-out;
transition: .3s ease-in-out;
}
.taller img:hover {
-webkit-transition: .3s ease-in-out;
transition: .3s ease-in-out;
height: 300px !important;
}
figure {
width: 300px;
height: 300px;
//height: 200px;
margin: 1em;
padding: 0;
background: #fff;
overflow: hidden;
border: 1px solid #222;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.22/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<div >
<h2>ARGH</h2>
<div>
<span class="taller">
<figure>
<img ng-mouseenter="set_ratio(this)" draggable="true" class="thumb" id="{{tablet.created}}" title="{{ tablet.title }}" data-ng- src={{tablet.src}} />
</figure>
</span>
</div>
</div>
</div>

The answer can be found here.
is there a post render callback for Angular JS directive?
I simply wrapped it in a $timeout
$timeout(function(){
$scope.set_ratio = function (it) { //Find ratio and return style so that it can be used for zoom effect.
var img = new Image();
img.src = it.tablet.src;
console.log(it.tablet)
if(img.height>img.width){
var h = 300*(img.height/img.width)+'px'
return {
"height": h
}
}else{
var w = 300*(img.width/img.height)+'px'
return {
"width":w
}
}
}
}, 50)

Related

Video Follow on Mouse Move in React

I have this javascript function that autoplays a Vimeo video and attaches to the cursor when hovering over an element. I'm trying to figure out how to do this in React but I'm not having any luck. Originally, I was using Froogaloop and Vimeo player api which seemed to work just fine, but not in react.
Here is the code below
Script
document.getElementById("div").addEventListener("mousemove", function() {
myFunction(event);
});
var mouse;
var cursor = document.getElementById("cursor");
function myFunction(e) {
mouseX = e.clientX;
mouseY = e.clientY;
cursor.style.left = (mouseX - 55) + "px";
cursor.style.top = (mouseY - 55) + "px";
}
// play video on hover
const iframe = document.getElementById('video');
// $f == Froogaloop
const player = $f(iframe);
// bind events
var mouseEntering = document.getElementById("div");
mouseEntering.addEventListener("mouseenter", function() {
player.api("play");
});
var mouseLeaving = document.getElementById("div");
mouseLeaving.addEventListener("mouseleave", function() {
player.api("unload");
});
HTML
<div id="div" class="div">
<div id="cursor" class="cursor">
<iframe id="video" class="video" src="https://player.vimeo.com/video/649593940?api=1&loop=1&muted=1" width="400px"
height="300px" frameborder="0"></iframe>
</div>
</div>
CSS
.wrapper{
display:grid;
grid-template-columns: repeat(auto-fit, minmax(241px,1fr));
gap: 20px;
}
.div:hover{
cursor:pointer;
}
.div:hover .cursor{
visibility:visible;
}
.cursor {
visibility:hidden;
position: absolute;
transform:translate(80px,50px);
background:red;
backface-visibility: hidden;
z-index: 9999999;
pointer-events: none; /* pointer-events: none is needed */
cursor: none;
}
.div {
background: blue;
height:200px;
width:100%;
cursor: none;
}
Here is a full working example on codepen
Any help on how to do this in react would be awesome.

Flickering issue when drawing div in Angular

In this plunk I have a div that needs to be drawn dragging the mouse. I'm using mouse up/move/down events. The problem that I have is that the div "flickers" when I drag down. How to fix this?
HTML
<style>
.frame {
width: 300px;
height: 300px;
background-color: orange;
position: relative;
}
.sel{
border:2px solid black;
background-color: #ffffff;
position:absolute;
}
</style>
<div class="frame" ng-mousedown="mouseDown($event)"
ng-mousemove="mouseMove($event)"
ng-mouseup="mouseUp()">
<div class="sel" ng-show="show"
ng-style="{top:selTop+'px', left:selLeft+'px', width:selWidth+'px', height:selHeight+'px'}"></div>
</div>
Javascript:
var app = angular.module('app', []);
app.controller('ctl', function ($scope) {
$scope.startPoint = {};
$scope.show = false;
$scope.mouseDown = function(event) {
$scope.startPoint = { x: event.offsetX, y: event.offsetY };
$scope.show = true;
};
$scope.mouseMove = function(event) {
if (!$scope.startPoint.x)
return;
$scope.selTop = $scope.startPoint.y;
$scope.selLeft = $scope.startPoint.x;
$scope.selHeight = event.offsetY - $scope.startPoint.y;
$scope.selWidth = event.offsetX - $scope.startPoint.x;
};
$scope.mouseUp = function() {
$scope.startPoint = {};
$scope.show = false;
};
});
just add these css properties
border: none;
color: transparent;
text-shadow: 0 0 0 gray;
text-align: center;
&:focus {
outline: none;
}
see the plunker http://embed.plnkr.co/9WntGyBLQxYSxkFjDAy2/

simple css animation not working on dynamic reactjs element

Check the snippet at codepen http://codepen.io/anon/pen/EZJjNO
Click on Add button, it will add another item but its appearing immediately without any fade effect.
JS:
class App extends React.Component {
constructor(props) {
super(props);
this.addItem = this.addItem.bind(this);
this.state = {
items: [1,2,3]
}
}
addItem() {
var items = this.state.items;
items.push(4);
this.setState({
items: items
})
}
render() {
return (
<div className="App">
{
this.state.items.map(function(i) {
return <div className="item fade">Testing</div>
})
}
<button onClick={this.addItem}>Add</button>
</div>
);
}
}
React.render(<App />, document.body);
CSS:
.App {
background-color: rgba(0,0,0, 0.5);
text-align: center;
height: 100vh;
}
div.item {
border: 1px solid #ccc;
padding: 10px;
background: #123456;
color: #fff;
opacity: 0;
transition: all 0.4s ease-out;
}
.fade {
opacity: 1 !important;
}
Since the fade class is added by default, you don't get the transition effect. If you open your browser's developer tools and remove the class, you'll see it fade away nicely.
There's a few ways to get what you want, but I'd just use a keyframe CSS animation like so:
.fade {
animation: 0.4s ease-out fadeIn 1;
}
#keyframes fadeIn {
0% {
opacity: 0;
visibility: hidden;
}
100% {
opacity: 1;
visibility: visible;
}
}
Here's a fork of your code pen showing how it works :)

How can I make the width of my div expand with it's content within a tooltip?

I have functioning tooltip that I would like to make responsive based on it's content. According to the inspector it is inheriting a width of 75px (I do not know from where) and then all the content is wrapping to the next line. I noticed if I change the positioning to relative all the text will go on one line, however I need to keep it's positioning absolute in order to make it appear above the svg. How can I achieve this?
I've already tried "display: inline-block" and using"float". Neither works.
<div class="tooltip-wrapper">
<div class='tooltip'>
{{content}}
</div>
<svg>
<use xlink:href={{icon}}>
</svg>
</div>
.tooltip-wrapper {
position: relative;
cursor: pointer;
svg {
height: 0.75rem;
width: 0.75rem;
}
&:hover {
.tooltip {
opacity: 1;
}
}
}
.tooltip {
position: absolute;
bottom: calc(100% + 1rem);
background-color: rgba($black, 0.7);
border-radius: 2px;
color: $white;
padding: 0.5rem;
opacity: 0;
transition: opacity $transition-timing ease;
&:before {
content: '';
position: absolute;
bottom: -0.3rem;
left: 50%;
margin-left: -0.3rem;
#include triangle(0.7rem, rgba($black, 0.7), down);
}
}
controller: ($element, $scope) ->
$scope.content = "Content stuff and stuff"
$scope.icon = "#icon-history"
link: (scope, element, attrs, ctrl) ->
#tooltipWrapper = element[0].children[0]
#tooltipIcon = #tooltipWrapper.children[1]
#tooltipContent = #tooltipWrapper.children[0]
console.log #tooltipWrapper
element.on 'mouseenter', =>
#_dropdown()
#_dropdown = (offset) =>
Lcontent = #tooltipContent.innerHTML
Lsplit = Lcontent.split(' ')
leng = 0
for i of Lsplit
leng += Lsplit[i].length
# console.log leng
if leng > 10
$(tooltipContent).css('width', #tooltipContent.width)
console.log #tooltipContent.width //this returns undefined
#left = -(#tooltipContent.offsetWidth - #tooltipIcon.offsetWidth) / 2
$(tooltipContent).css('left', #left)
This is what I was missing.
'white-space: nowrap;' inside of my 'tooltip' class. I then added the code below inside of my '#_dropdown function so it would eventually go on multiple lines if I had tons of content.
if leng > 50
$(tooltipContent).css({"width": '360px', 'white-space': 'initial'})

How does one create an accordion dropdown menu in Angular Material?

If you go to the https://material.angularjs.org website,
you will notice a very nice Accordion dropdown menu in the sidenav.
I'm trying to find a simplified version of this feature.
I've looked into many examples it appears many of them are no longer working.
I don't need it complicated. So no need of repetative items. I can do all that. I need the basic functionality.
From what I've researched they have an expando feature being developed, but until then is there a work around?
Updated:
I wasn't able to find a good angular material design, but I was able to find an angular method.
https://github.com/sherwaniusman/angular-accordion
The following fiddle really helped me:
Accordion example
I have also added functionality which allows expanding only 1 menu at a time, if others opened, it will automatically close them.
Code in Controller:
function controller($scope) {
$scope.accordianData = [
{ "heading" : "About Us", "content" : "" },
{ "heading" : "Terms of Use", "content" : "" },
{ "heading" : "Privacy Policy", "content" : "" },
{ "heading" : "Help", "content" : "" },
];
);
// To expand or collapse the current view
//This functionality automatically closes the other expanded lists
$scope.toggleView = function(ary, data, index){
for(var i=0; i<ary.length; i++){
if(i!=index) { ary[i].expanded=false; }
else { data.expanded=!data.expanded; }
}
}
}
And the view/html Code is:
Just tweaked a bit of functionality as per my requirements:
<md-content id="dynamic-content" class="f-clear-padding">
<div class="md-accordion" ng-repeat="data in accordianData">
<!-- <md-toolbar ng-init="data.expanded = false" ng-click="data.expanded = !data.expanded"> this was the code in demo-->
<md-toolbar ng-init="data.expanded = false" ng-click="toggleView(accordianData, data, $index)">
<div class="md-toolbar-tools">
<!-- <h2> -->
<div ng-bind="data.heading"></div>
<!-- </h2> -->
<div flex=""></div>
<div ng-class="{expandCollapse:true, active:data.expanded}"></div>
</div>
</md-toolbar>
<div style="overflow:scroll" ng-class="{dataContent:true, activeContent:data.expanded}">
<div style="padding:10px" ng-bind-html="data.content"></div>
</div>
<div>
</md-content>
And the css part:
.md-accordion .expandCollapse { width:30px; height:30px; position:relative; font-size:20px; font-weight:bold; cursor:pointer; color:#fff; display:block; margin-top: -2px; margin-left: -2px; overflow:hidden; }
.md-accordion .expandCollapse:active { border:0px; }
.md-accordion .expandCollapse:before, .md-accordion .expandCollapse:after { width:30px; height:30px; display:block; position:absolute; top:0; left:0; line-height:32px; text-align:center; -webkit-transition: .3s all ease-out; transition: .3s all ease-out; }
.md-accordion .expandCollapse:before { opacity:1 -webkit-transform: rotate(0deg); transform: rotate(0deg); content: "|"; margin-top:-3px; }
.md-accordion .expandCollapse:after { opacity:1; -webkit-transform: rotate(-90deg); transform: rotate(-90deg); content: "|"; margin-left:-3px; }
.md-accordion .active:before { opacity:1; -webkit-transform: rotate(90deg); transform: rotate(90deg); margin-left:3px; margin-top:0px; }
.md-accordion .dataContent { background: #F2F2F2; height:0px; overflow:hidden; -webkit-transition: .3s all ease-out; transition: .3s all ease-out; }
.md-accordion .activeContent { height:60vh; padding:0; display:block; }
.md-accordion md-toolbar{ cursor:pointer; border-bottom:1px solid rgb(63,107,181) }
Here we have fixed the height of the expandable list in order to keep the list items still visible, else once you expand a div having a huge content the user may feel that it's the only list item available and may not be able to see the other items if any of the div is expanded, the overflow:scroll allows the view to be scroll through, else it will be stiff and the user won't be ablt to view the entire content.
Hope this is helpful... :)
So this is what I ended up using
Directive HTML Code
need a right and down arrow img
<ang-accordion one-at-a-time="true" icon-position="right" close-icon-url="<?php echo URL; ?>/img/icons/right-icon.png" open-icon-url="<?php echo URL; ?>/img/icons/down-icon.png">
<collapsible-item ng-repeat="item in items" item-title="" initially-open="">
<div>Text</div>
</collapsible-item>
</ang-accordion>
Script to include
<script type="text/javascript" src="<?php echo URL; ?>/js/angular/controllers/accordion.js"></script>
JS: accordion.js
app.controller('angAccordionController', ['$scope', function($scope){
var collapsibleItems = [];
this.openCollapsibleItem = function(collapsibleItemToOpen) {
if( $scope.oneAtATime ) {
angular.forEach(collapsibleItems, function(collapsibleItem) {
collapsibleItem.isOpenned = false;
collapsibleItem.icon = collapsibleItem.closeIcon;
});
}
collapsibleItemToOpen.isOpenned = true;
};
this.addCollapsibleItem = function(collapsibleItem) {
collapsibleItems.push(collapsibleItem);
if ( $scope.closeIconClass !== undefined || $scope.openIconClass !== undefined ) {
collapsibleItem.iconsType = 'class';
collapsibleItem.closeIcon = $scope.closeIconClass;
collapsibleItem.openIcon = $scope.openIconClass;
}
else if ( $scope.closeIconUrl !== undefined || $scope.openIconUrl !== undefined ) {
collapsibleItem.iconsType = 'url';
collapsibleItem.closeIcon = $scope.closeIconUrl;
collapsibleItem.openIcon = $scope.openIconUrl;
}
collapsibleItem.iconIsOnLeft = $scope.iconPosition == 'left' ? true: false;
};
}])
.directive('angAccordion', function() {
return {
restrict: 'EA',
transclude: true,
replace: true,
scope: {
oneAtATime: '#',
closeIconUrl: '#',
openIconUrl: '#',
closeIconClass: '#',
openIconClass: '#',
iconPosition: '#'
},
controller: 'angAccordionController',
template: '<div class="accordion" ng-transclude></div>'
};
});
angular.module('collapsibleItem', []).directive('collapsibleItem', function() {
return {
require: '^angAccordion',
restrict: 'EA',
transclude: true,
replace: true,
scope: {
itemTitle: '#',
itemDisabled: '=',
initiallyOpen: '#'
},
link: function(scope, element, attrs, accordionController) {
scope.isOpenned = (scope.initiallyOpen == "true") ? true : false;
accordionController.addCollapsibleItem(scope);
if(scope.isOpenned)
scope.icon = scope.openIcon;
else
scope.icon = scope.closeIcon;
scope.toggleCollapsibleItem = function () {
if(scope.itemDisabled)
return;
if(!scope.isOpenned) {
accordionController.openCollapsibleItem(this);
scope.icon = scope.openIcon;
}
else {
scope.isOpenned = false;
scope.icon = scope.closeIcon;
}
};
scope.getIconUrl = function ( type ) {
return type == 'url' ? scope.icon : null;
};
},
template: '<div class="collapsible-item" ng-class="{open: isOpenned}"><div class="title" ng-class="{disabled: itemDisabled}" ng-click="toggleCollapsibleItem()">{{itemTitle | limitTo:28 }}<i ng-show="iconsType == \'class\'" class="{{icon}} icon" ng-class="{iconleft: iconIsOnLeft}"></i><img ng-show="iconsType == \'url\'" class="icon" ng-class="{iconleft: iconIsOnLeft}" ng-src="{{getIconUrl(iconsType)}}" /></div><div class="body"><div class="content" ng-transclude></div></div></div>'
};
});
CSS
.collapsible-item {
margin-bottom: 0px;
}
.collapsible-item .title {
padding: 10px;
background-color: #dfdfdf;
border: 0px solid #ccc;
cursor: pointer;
}
.collapsible-item .title .icon {
float: right;
height: 20px;
width: 20px;
font-size: 19px !important;
padding-right: 1px;
}
.collapsible-item .title .iconleft {
float: left !important;
}
.collapsible-item .title.disabled {
background: #eee;
color: #999;
cursor: text;
}
.collapsible-item .body {
position: relative;
top: -4px;
max-height: 0;
overflow: hidden;
border: 1px solid #ccc;
border-top: 0;
z-index: -1;
-webkit-transition: max-height 0.5s ease;
-moz-transition: max-height 0.5s ease;
-o-transition: max-height 0.5s ease;
transition: max-height 0.5s ease;
}
.collapsible-item .body .content {
padding: 5px 15px 5px 15px;
}
.collapsible-item.open .body {
max-height: 1000px;
z-index: 1;
}

Resources