i'm try to create a Polymer element with a clock, simple circle and handlers with animation in CSS, so i need to update the style transform: rotate in the element template. I test the code in AngularJs and it works well, but in Polymer i cannot update the CSS property every second, the value are setted only when the element is instatiate and it seems i use the wrong way to data binding the var hourDeg, minuteDeg, secondDeg with the degrees for the clock's handlers, here the code :
// HTML code
<link rel="import" href="../../bower_components/polymer/polymer.html">
<script src="../../bower_components/moment/moment.js"></script>
<polymer-element name="my-clock" attributes="">
<template>
<link rel="stylesheet" href="my-clock.css">
<div id="hour" class="hero-hour" style="transform: rotate({{hourDeg + 'deg'}});">
</div>
<div id="minute" class="hero-minute" style="transform:rotate({{getMinuteDegree()}});"></div>
<div id="second" class="hero-second" style="transform: rotate({{secondDeg + 'deg'}});"></div>
</template>
<script>
... // script reported down
</script>
</polymer-element>
// javascript code
(function () {
var hour, minute, second;
// function for update the Clock
function updateClock(){
var now = moment();
second = now.seconds() * 6,
minute = now.minutes() * 6 + second / 60,
hour = ((now.hours() % 12) / 12) * 360 + 90 + minute / 12;
console.log(second);
}
// setTimeout to update the Clock every 1000 ms
function timedUpdate () {
updateClock();
setTimeout(timedUpdate, 1000);
}
// ok timedUpdate
timedUpdate();
Polymer({
// define element prototype here
getHourDegree: function(){
return hour + 'deg';
},
getMinuteDegree: function(){
return minute + 'deg';
},
getSecondDegree: function(){
return second + 'deg';
},
hourDeg : this.hour,
secondDeg : this.second
});
})();
so i try different solution to pass the values to div elements but i cannot update the values every 1000 ms !
You need to update attributes on the element to trigger a re-render. The element isn't watching for changes to hour, minute, second, so it doesn't re-render itself when you update those values.
Here is an example that will do what you're trying to achieve.
<div id="hour" style="transform: rotate({{hour + 'deg'}});"></div>
<div id="minute" style="transform: rotate({{minute + 'deg'}});"></div>
<div id="second" style="transform: rotate({{second + 'deg'}});"></div>
....
Polymer({
hour : 0,
minute : 0,
second : 0
domReady: function(){
// start the clock when dom is ready
setInterval(this.updateClock.bind(this), 1000);
},
updateClock: function(){
var now = moment();
// update properties to trigger re-render
this.second = now.seconds() * 6,
this.minute = now.minutes() * 6 + this.second / 60,
this.hour = ((now.hours() % 12) / 12) * 360 + 90 + this.minute / 12;
},
});
And here's the clock element updated to Polymer 3:
<html>
<body>
<script type="module">
import { PolymerElement, html } from 'https://unpkg.com/#polymer/polymer/polymer-element.js?module'
class MyClock extends PolymerElement {
static get properties () {
return {
hour: Number,
minute: Number,
second: Number
}
}
ready () {
super.ready()
this.updateClock()
}
updateClock () {
let now = new Date()
this.second = now.getSeconds() * 6
this.minute = now.getMinutes() * 6 + this.second / 60
this.hour = ((now.getHours() % 12) / 12) * 360 + 90 + this.minute / 12
setTimeout(() => this.updateClock(), 1000)
}
static get template () {
return html`
<link rel="stylesheet" href="my-clock.css">
<div id="hour" class="hero-hour" style="transform: rotate([[hour + 'deg']]);"></div>
<div id="minute" class="hero-minute" style="transform: rotate([[minute + 'deg']]);"></div>
<div id="second" class="hero-second" style="transform: rotate([[second + 'deg']]);"></div>
`
}
}
customElements.define('my-clock', MyClock)
</script>
<my-clock></my-clock>
</body>
</html>
This example works without polyfills in Chrome and Firefox, you can try this in CodePen.
Related
I have an audio player on a page. I have a custom progress bar that I created. When users click on that progress bar, I want to tell the audio player to start playing at that position. How do I detect the position of where the person clicked in the div and translate that to a position to play the song?
var aud = $('audio')[0];
var index = 0;
$('.play').on('click', function() {
aud.src = $(this).attr('data-url');
index = $(this).index('.play');
if (aud.paused) {
aud.play();
} else {
aud.pause();
}
});
aud.ontimeupdate = function() {
$('.progress-bar-wrapper').hide();
$('.progress-bar-wrapper:eq(' + index + ')').show();
$('.progress:eq(' + index + ')').css('width', aud.currentTime / aud.duration * 100 + '%')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<audio src="" style="display: none"></audio>
<div class="play" data-url="{{url}}"><img src="/images/play_course.png" /></div>
<div class="progress-bar-wrapper">
<div class="progress"></div>
</div>
You can get the position of the mouse inside the click event.
I have added two variables inside the click event with both the position of the mouse relative to the element itself and the percentage that the click position represent, use them as you see fit.
Note: I added width and height for the progress bar so I can test your code so keep in mind that the progress wrapper needs to be visible and have a width value.
var aud = $('audio')[0];
var index = 0;
$('.play').on('click', function() {
aud.src = $(this).attr('data-url');
index = $(this).index('.play');
if (aud.paused) {
aud.play();
} else {
aud.pause();
}
});
$(".progress-bar-wrapper").on("click", function (e) {
var posX = e.pageX - $(this).offset().left;
var percentage = parseInt((posX / $(this).width()) * 100);
});
aud.ontimeupdate = function() {
$('.progress-bar-wrapper').hide();
$('.progress-bar-wrapper:eq(' + index + ')').show();
$('.progress:eq(' + index + ')').css('width', aud.currentTime / aud.duration * 100 + '%')
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<audio src="" style="display: none"></audio>
<div class="play" data-url="{{url}}"><img src="https://cdn.iconscout.com/icon/premium/png-512-thumb/play-button-1516951-1285078.png" /></div>
<div class="progress-bar-wrapper">
<div class="progress"></div>
</div>
I have a countdown timer:
interval;
timeJump: number = 10;
timeLength: number = 5;
timerLeft: number = this.timeLength;
startTimer() {
this.interval = setInterval(() => {
if (this.timerLeft - this.timeJump / 1000 <= 0) {
this.timerLeft = 0;
clearInterval(this.interval);
return;
setTimeout(() => this.finishedTimer(), 500);
} else {
this.timerLeft -= this.timeJump / 1000;
}
}, this.timeJump);
}
I am calling the startTimer() function in ngOnInit(), and it is running.
In the html template I have a progress bar:
<div class="progress-wrapper" style="height: 4%;">
<mat-progress-bar id="timer" mode="determinate" value="{{(timerLeft/timeLength) * 100}}"
style="height: 100%;"></mat-progress-bar>
</div>
When the timer reaches the end, about the last 10%, it goes down in value a lot quicker than it should.
Can you help me fix this?
Thanks
Is this a problem? Work perfect for me!
(except for setTimeout(() => this.finishedTimer(), 500); after return; never be used)
But, you can use RxJs for that!
HTML
<mat-progress-bar mode="determinate" [value]="progressbarValue"></mat-progress-bar>
TS
import { interval } from 'rxjs';
...
progressbarValue = 100;
curSec: number = 0;
startTimer(seconds: number) {
const time = seconds;
const timer$ = interval(1000);
const sub = timer$.subscribe((sec) => {
this.progressbarValue = 100 - sec * 100 / seconds;
this.curSec = sec;
if (this.curSec === seconds) {
sub.unsubscribe();
}
});
}
And call startTime method with progress bar timeout.
startTimer(120) // 120 seconds
References: https://stackblitz.com/edit/angular-material-progress-bar-decrease
I fixed it!
The problem was I was using too short of an interval. The progress bar couldn't keep up with the speed. Set the interval to whatever you want, but always round the value you put in the value of the progress bar to a whole number, like this:
timeLeft | number : '1.0-0'
Here is my angular code to define a countdown timer for my website:
function AlbumCtrl($scope, $timeout) {
$scope.counter = new Date("Nov 30, 2017 09:00:00").getTime() - new Date().getTime();
$scope.onTimeout = function() {
$scope.counter--;
var distance = $scope.counter;
$scope.countdown = Math.floor(distance / (1000 * 60 * 60 * 24)) + "d " +
Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) + "h " +
Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)) + "m " +
Math.floor(((distance % (1000 * 60)) / 1000)) + "s ";
if ($scope.counter > 0) {
mytimeout = $timeout($scope.onTimeout, 1000);
} else {
alert("Time is up!");
}
}
var mytimeout = $timeout($scope.onTimeout, 1000);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <!-- added for a working snippet -->
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.0rc11.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
</head>
<body>
<div ng-controller="AlbumCtrl">
{{counter}} {{countdown}}
</div>
</body>
</html>
The 'counter' appears to decrement correctly on my page but the 'countdown' stays fixed and shows the correct value only on page load or refresh. For example, I get the following output as time passes:
1115148833 12d 21h 45m 48s
1115148745 12d 21h 45m 48s
1115148693 12d 21h 45m 48s
What exactly is going wrong? Thanks for your help.
https://github.com/johnpapa/angular-styleguide/tree/master/a1
// Declare a "main" module for the single page application
// second argument to module is an array of dependencies your module
// uses (examples: ui-router ui-bootstrap etc., 'ng' module is assumed as a dependency)
angular.module('myApp', [])
//Defining a service on the module allows you to encapsulate some functionality
//Injecting the angular $timeout service from the 'ng' module and defining
//a new service. This object gets initialized the first time it's
//referenced and stays alive until the application/page is refreshed.
.service("CountdownService", function($timeout){
//Define an object to attach properties and functions to and return for
//use elsewhere in the app
var countdownService = {};
//Basically the functional code you had already adjusted the
//seconds to just use %60 since some sort of issue with the existing math
// %60 should give remainder after dividing by 60 and appears the "distance"
// was moving at 1 second per second not 1000, so removed the 1000 factors
countdownService.counter = new Date("Nov 30, 2017 09:00:00").getTime() - new Date().getTime();
countdownService.onTimeout = function() {
countdownService.counter--;
var distance = countdownService.counter;
var newValue = Math.floor(distance / (1000 * 60 * 60 * 24)) + "d " +
Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)) + "h " +
Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)) + "m " +
Math.floor((distance % (60)) ) + "s ";
countdownService.countdown = newValue;
console.log(Math.floor((distance % (60))) + "s ");
if (countdownService.counter > 0) {
mytimeout = $timeout(countdownService.onTimeout, 1000);
} else {
alert("Time is up!");
}
}
var mytimeout = $timeout(countdownService.onTimeout, 1000);
return countdownService;
})
//Now the controller just has the new service injected it uses "this"
//instead of scope in the ng-controller we use "controllerAs" syntax
//so we don't need to use the scope (angular 2 hides the scope so good
//to just get used to not using it)
.controller("TestCtrl", function(CountdownService){
this.CountdownService = CountdownService;
});
<!doctype html>
<html ng-app="myApp">
<head>
</head>
<body>
<div ng-controller="TestCtrl as testCtrl">
{{testCtrl.CountdownService.counter}} {{testCtrl.CountdownService.countdown}}
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.6/angular.js"></script> <!-- added for a working snippet -->
</body>
</html>
This is interesting . Your Math.floor function is giving round figure of the fraction and your fraction number changes so minutely with counter , only seconds string decreases from one number when counter comes to 1095904999. So you are not able to notice the change , see yourself :
function AlbumCtrl($scope, $timeout) {
$scope.counter = new Date("Nov 30, 2017 09:00:00").getTime() - new Date().getTime();
$scope.onTimeout = function() {
--$scope.counter;
var distance = $scope.counter;
$scope.countdown = $scope.counter/ (1000 * 60 * 60 * 24) + "d " +
( $scope.counter% (1000 * 60 * 60 * 24)) / (1000 * 60 * 60) + "h " +
( $scope.counter% (1000 * 60 * 60)) / (1000 * 60)+ "m " +
(( $scope.counter% (1000 * 60)) / 1000) + "s ";
if ($scope.counter > 0) {
mytimeout = $timeout($scope.onTimeout, 1000);
} else {
alert("Time is up!");
}
}
var mytimeout = $timeout($scope.onTimeout, 1000);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <!-- added for a working snippet -->
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.0rc11.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
</head>
<body>
<div ng-controller="AlbumCtrl">
{{counter}} {{countdown}}
</div>
</body>
</html>
For demonstration purposes(to simplify correctness of answer checking) I set destination date to 1 day and 2 hours later from now:
function AlbumCtrl($scope, $timeout) {
var date = new Date();
date.setHours(date.getHours() + 2);
date.setDate(date.getDate() + 1);
$scope.counter = date.getTime() - new Date().getTime();
$scope.onTimeout = function() {
$scope.counter = $scope.counter - 1000;
var ost = $scope.counter;
function GetPart(koef) {
koef *= 1000;
var res = Math.floor(ost / koef);
ost = ost - res * koef;
return res;
}
var days = GetPart(60 * 60 * 24);
var hours = GetPart(60 * 60);
var min = GetPart(60);
var sec = GetPart(1);
$scope.countdown = days + "d " + hours + "h " + min + "m " + sec + "s ";
if ($scope.counter > 0)
mytimeout = $timeout($scope.onTimeout, 1000);
else
alert("Time is up!");
}
var mytimeout = $timeout($scope.onTimeout, 1000);
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<!-- added for a working snippet -->
<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.0rc11.min.js"></script>
<script src="http://documentcloud.github.com/underscore/underscore-min.js"></script>
</head>
<body>
<div ng-controller="AlbumCtrl">
{{countdown}}
</div>
</body>
</html>
I have access to a bitcoin websocket which is giving me the current market price for the bitcoin every minute. I want to make a single rectangle that changes dynamically to these values being currently passed into my bardata array.
Here is the code:
<html>
<script type="text/javascript" src="js/d3.min.js"></script>
<script src="https://code.jquery.com/jquery-2.2.4.min.js" integrity="sha256-BbhdlvQf/xTY9gja0Dq3HiwQF8LaCRTXxZKRutelT44=" crossorigin="anonymous"></script>
<body>
<script>
var webSocket = new WebSocket('wss://socket.cbix.ca/index'); //creating a websocket connection to blockhain.info
webSocket.onopen = function (event) {
console.log('Websocket connection open');
};
var bardata = [];
webSocket.onmessage = function (event) {
var e = jQuery.parseJSON(event.data);
console.log(e);
var high = e.high;
console.log(high);
update();
bardata.push(high);
$('.message').prepend("<p>" + "The highest bitcoin value for this minute is: $" + high + "</p>");
}
var height = 730
, width = 1080
, barWidth = 50
, barOffset = 5;
var yScale = d3.scale.linear()
.domain([0, d3.max(bardata + 100)])
.range([0, height])
var svg = d3.select('body').append('svg')
.attr('width', width)
.attr('height', height)
.style('background', '#C9D7D6')
function update() {
svg.selectAll('rect').data(bardata)
.enter().append('rect')
.style('fill', '#C61C6F')
.attr('width', barWidth)
.attr('height', function (d) {
return yScale(d);
})
.attr('x', function (d, i) {
return i * (barWidth + barOffset);
})
.attr('y', function (d) {
return height - yScale(d);
}).transition()
.attr('height', function (d) {
return yScale(d);
})
.attr('y', function (d) {
return height - yScale(d);
})
.delay(function (d, i) {
return i * 20;
})
.duration(1000)
.ease('elastic')
}
</script>
<div class="message">
</div>
</body>
</html>
However when I run this code a new bar is created every time. I know this is due to the update function re drawing everything once the .onmessage function updates again. I was wondering if there was another way to go about this maybe using AJAX or something? Some help would be greatly appreciated.
The problem you're facing is that your update function has only an "enter" selection. In this situation, the expected behaviour is having only 1 non-changing bar (because the "enter" selection is empty from the second time on). But as you're pushing values to barData, the length of this barData array is increasing, and the bars you're seeing are the new "enter" selections.
So, the first change is using just the last value of barData, not the whole array.
The second change is writing a proper enter and update selections. This is how you should do. First, bind the data:
var rect = svg.selectAll("rect")
.data([data]);
Then, create your "enter" selection:
rect.enter()
.append("rect").attr("x", 0)
.attr("y", 25)
.attr("height", 50)
.attr("width", d => scale(d))
.attr("fill", "#C61C6F");
This "enter" selection will work only the first time you call update. From the second time on, you'll only change the existing bar:
rect.transition()
.duration(200)
.ease('elastic')
.attr("width", d => scale(d));
Here is a demo:
setInterval(() => {
var data = ~~(Math.random() * 100)+1;
update(data);
}, 2000);
var svg = d3.select("#svg")
.append("svg")
.attr("width", 300)
.attr("height", 100);
var text = d3.select("#text");
var scale = d3.scale.linear()
.range([0, 300])
.domain([0, 100]);
update(50);
function update(data) {
var rect = svg.selectAll("rect")
.data([data]);
rect.enter()
.append("rect").attr("x", 0)
.attr("y", 25)
.attr("height", 50)
.attr("width", d => scale(d))
.attr("fill", "#C61C6F");
rect.transition().duration(500).ease('elastic').attr("width", d => scale(d));
text.text("The highest bitcoin value for this minute is: $" + data)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="text"></div>
<div id="svg"></div>
I am trying to display multiple dialogs on the same page. It is for a list of my android apps on a single page, and I found that dialogs size according to the window size. I need them to not be on top of eachother, but can't figure out if that is possible.
http://dunriteapps.com/material.htm#apps
<section>
<paper-dialog transition="paper-dialog-transition-bottom" opened="true">
...
</paper-dialog>
</section>
<section>
<paper-dialog transition="paper-dialog-transition-bottom" opened="true">
...
</paper-dialog>
</section>
I feel like I should be using something other than dialogs. :\
It sounds like you're looking for a separate raised container for each app description, and that you want those positioned in a single column. If so, I think the element is what you want. Cards can be swipeable or static
To install in your app:
bower install Polymer/polymer-ui-card2. Import:
To Import:
There's currently a demo linked here if you'd like to see the card look and behavior: http://www.polymer-project.org/components/polymer-ui-card/demo.html
Example from GitHub:
<!--
/**
* #module Polymer UI Elements
*/
/**
* polymer-ui-card <b>(WIP)</b>
*
* Example:
*
* <polymer-ui-card>
* ...
* </polymer-ui-card>
*
* #class polymer-ui-card
*/
/**
* Fired when the card is swiped away.
*
* #event polymer-card-swipe-away
*/
-->
<link rel="import" href="../polymer/polymer.html">
<polymer-element name="polymer-ui-card" attributes="swipeable noCurve"
on-trackstart="{{trackStart}}" on-track="{{track}}" on-trackend="{{trackEnd}}" on-flick="{{flick}}">
<template>
<link rel="stylesheet" href="polymer-ui-card.css">
<content></content>
</template>
<script>
Polymer('polymer-ui-card', {
/**
* If true, the card can be swiped.
*
* #attribute swipeable
* #type boolean
* #default false
*/
swipeable: false,
noCurve: false,
offsetRatio: 0.2,
widthRatio: 3,
ready: function() {
this.setAttribute('touch-action', 'pan-y');
this.transitionEndListener = this.transitionEnd.bind(this);
},
leftView: function() {
this.removeListeners();
},
addListeners: function() {
this.addEventListener('webkitTransitionEnd',
this.transitionEndListener);
this.addEventListener('transitionend', this.transitionEndListener);
},
removeListeners: function() {
this.removeEventListener('webkitTransitionEnd',
this.transitionEndListener);
this.removeEventListener('transitionend', this.transitionEndListener);
},
swipeableChanged: function() {
if (this.swipeable) {
this.addListeners();
} else {
this.removeListeners();
}
},
animate: function(x) {
var s = this.style;
var d = x > 0 ? 1 : -1;
var w = this.w * this.widthRatio;
var x1 = Math.max(0, Math.abs(x) - this.w * this.offsetRatio);
var r = Math.max(0, (w - x1) / w);
var y = w - Math.sqrt(w * w - x1 * x1);
var deg = (1 - r) * d * 90;
s.opacity = r;
var translate = 'translate3d(' + x + 'px,' +
(this.noCurve ? 0 : y) + 'px,0)';
var rotate = 'rotate(' + deg + 'deg)';
s.webkitTransform = s.mozTransform = s.msTransform = s.transform =
translate + (this.noCurve ? '' : ' ' + rotate);
},
trackStart: function(e) {
if (this.swipeable) {
e.preventTap();
this.w = this.offsetWidth;
this.classList.add('dragging');
}
},
track: function(e) {
if (this.swipeable) {
this.animate(e.dx);
}
},
trackEnd: function(e) {
if (this.swipeable) {
this.swipeEnd(Math.abs(e.dx) > this.w / 2 && e.dx * e.xDirection > 0,
e.dx > 0);
}
},
flick: function(e) {
if (this.swipeable && !this.away) {
var v = e.xVelocity;
this.swipeEnd(Math.abs(v) > 2, v > 0);
}
},
swipeEnd: function(away, dir) {
this.classList.remove('dragging');
this.away = away;
if (away) {
var w = this.w * this.widthRatio;
this.animate(dir ? w : -w);
} else {
this.animate(0);
}
},
transitionEnd: function() {
if (this.away) {
this.fire('polymer-card-swipe-away');
}
}
});
</script>
</polymer-element>
I think you are using the wrong element.
If you want the shadow effect, you can just combine normal divs with paper-shadow and core-animated-pages for transition effects