Related
I'm trying to have a banner in the corner of my website with curved and animated (infinite banner style) text inside a responsive div.
Something like this:
I'm a rookie at programming and also tried doing it with .svg's based on other's examples, but with little luck.
The original curve in photoshop is an "Arc upper" (+47%)
Any help much appreciated!
A
I'm not sure if this is what you want or not, but hope it works:
function curvedText(time) {
var tl = new TimelineMax({ repeat: -1 });
var text = document.querySelector("svg textpath"),
path = document.querySelector("svg defs path");
var from = {
transformOrigin: "center center",
rotation: 0,
};
var to = {
rotation: -360,
ease: Linear.easeInOut,
};
tl.fromTo([text, path], time, from, to);
return tl;
}
curvedText(20);
body {
position: relative;
}
div {
position: fixed;
right: 0;
bottom: 0;
width: 400px;
height: 200px;
/* border: 1px solid black; */
overflow: hidden;
}
svg:not(:root) {
overflow: visible;
}
svg {
position: absolute;
right: 50px;
bottom: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.14.2/TweenMax.min.js"></script>
<div>
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="150 -400 600 600">
<defs>
<path
id="txt-path"
d="M50, 800a800, 800 0 1, 1 1600, 0a800, 800 0 1, 1 -1600, 0"></path>
</defs>
<text
fill="red"
font-size="70px"
font-family="Helvetica Neue"
font-weight="600">
<textPath startOffset="1000" xlink:href="#txt-path">
I'm trying to have a banner in the corner of my website with curved
and animated (infinite banner style) text inside a responsive div.
</textPath>
</text>
</svg>
</div>
Don't forget to add CDN to your codes:
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.14.2/TweenMax.min.js"></script>
The design is for a website built on Angular . Have been using canvas along with html . canvas is used for building the dotted and solid arrows around circle . html is used for designing circles.
HTML :
<div class="process-card-container">
<div [style.margin-left.px]="canvasDivMargin" class="myCanvasDiv">
<canvas id="myCanvas" width="2000" height="600">
Your browser does not support the HTML5 canvas tag.</canvas>
</div>
<div class="all-cards">
<div class="card-container" *ngFor="let card of data.cards;">
<div (click)='onCTAClick(card.callToActions)'
[ngClass]="{'each-process-card-without-pointer': card.callToActions == undefined, 'each-process-card' : card.callToActions !== undefined }"
[ngStyle]="card.bgStyle">
<div class="each-card-content-container">
<span class="each-card-num">{{card.cardNumber}}</span>
<span class="each-card-title">{{card.cardTitle}}</span>
</div>
</div>
</div>
</div>
</div>
SASS
.process-card-container {
margin-top: 50px;
margin-bottom: 20px;
text-align: center;
}
.myCanvasDiv{
padding-left: 0;
padding-right: 0;
margin-right: auto;
display: block;
}
.card-container {
display: inline-block;
margin-right: 16px;
margin-left: 75px;
}
.all-cards {
margin-left: -8rem;
margin-top: -553px;
}
.each-process-card {
height: 250px;
width: 250px;
border-radius: 50%;
text-align: center;
cursor: pointer;
}
.each-process-card-without-pointer {
height: 250px;
width: 250px;
border-radius: 50%;
text-align: center;
}
.each-card-content-container {
position: relative;
}
.each-card-num {
position: absolute;
color: #ffffff;
font-size: 54px;
top: 30px;
left: 110px;
padding-left: 0;
padding-right: 0;
margin-left: 0;
margin-right: 0;
display: block;
}
.each-card-title {
font-size: 22px;
font-family: "Metropolis", "Helvetica Neue", Helvetica, Arial, sans-serif;
font-weight: bold;
color: white;
letter-spacing: 0.8px;
width: 120px;
position: absolute;
top: 109px;
left: 65px;
height: 20px;
}
The design works fine on one screen but it breaks on screens with other resolutions , also when zooming in /out , the arrows and circles go here and there .
TS file
import { Component, Input, OnInit } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'app-process-card',
templateUrl: './process-card.component.html',
styleUrls: ['./process-card.component.scss']
})
export class ProcessCardComponent implements OnInit {
constructor(public router: Router) { }
#Input()
public data;
public arrowColorArr = [];
public dia = 170;
public top_position = 180;
public left_position = 180;
public c;
public ctx;
public canvasDiv;
public canvasDivMargin = 36;
ngOnInit() {
this.arrowColorArr = this.data.cards.map((cardIn) => {
var eachBgColor = cardIn.bgStyle.background.includes("linear-gradient") ? cardIn.bgStyle.background.substring(35, 43) : cardIn.bgStyle.background;
return eachBgColor.replace(/ /g, '')
})
this.c = document.getElementById('myCanvas');
this.ctx = this.c.getContext("2d");
this.ctx.setLineDash([0.5, 15]);
this.ctx.lineWidth = 7;
this.ctx.lineCap = "round";
//1st down
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.arc(this.left_position, this.top_position + 4, this.dia, 0, 1 * Math.PI);
this.ctx.stroke();
//second circle up
this.ctx.strokeStyle = "#0091da";
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 3.1, 2 * Math.PI);
this.ctx.stroke();
//3rd circle down
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 0, 1 * Math.PI);
this.ctx.stroke();
//4th circle up
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 3.15, 1.98 * Math.PI);
this.ctx.stroke();
this.ctx.lineWidth = 5;
//1st up
this.ctx.setLineDash([0, 0]);
this.ctx.strokeStyle = this.arrowColorArr[0];
this.ctx.beginPath();
this.ctx.arc(this.left_position, this.top_position, this.dia, 3.3, 1.8 * Math.PI);
this.ctx.stroke();
//2nd down
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 0.5, 0.9 * Math.PI);
this.ctx.stroke();
//3rd up
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 3.5, 1.8 * Math.PI);
this.ctx.stroke();
//4th down
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 0.1, 0.9 * Math.PI);
this.ctx.stroke()
//arrow mark
this.ctx.strokeStyle = this.arrowColorArr[0];
this.ctx.beginPath();
this.ctx.moveTo(330, 92);
this.ctx.lineTo(327, 79);
this.ctx.lineTo(317, 89);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.moveTo(677, 248);
this.ctx.lineTo(678, 262);
this.ctx.lineTo(667, 256);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.moveTo(1011, 90);
this.ctx.lineTo(1007, 77);
this.ctx.lineTo(997, 87);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.moveTo(1370, 183);
this.ctx.lineTo(1375, 193);
this.ctx.lineTo(1365, 193);
this.ctx.closePath();
this.ctx.stroke();
}
}
You can get very, very close with just plain HTML and CSS! I've done this using an ordered list, border-radius and border-style, plus a tiny SVG for the pointer arrow. This has a few advantages:
##Edit - see below for a version using SVG for all the decorative elements...
Much simpler code
Uses the browser's layout and rendering engine
Semantically correct HTML and live text
Automatic step numbering
Automatic coloring based on list position
Able to resize/respond as required
Able to easily add/remove steps as required
The downside is mainly that you don't have absolute fine control over the pixel placement, spacing and style of things like the dots and lines. I think this is a pretty reasonable trade-off for the advantages, but it will depend on your situation. For me, the result looks like this:
Here's the code
.disc-list {
counter-reset: step; /* Allows us to show the step number */
list-style: none;
padding: 0;
margin: 0;
display: flex;
flex-direction: row; /* Put the discs in a row */
flex-wrap: wrap; /* Allow them to wrap around as required */
}
.disc-list li {
counter-increment: step; /* Increment the list number */
position: relative;
width: 8.5rem;
height: 8.5rem;
text-align: center;
border-radius: 50%; /* Make it a circle */
background: #888;
color: #888;
display: flex;
flex-shrink: 0; /* Don't allow the discs to squash inside the container */
flex-direction: column;
align-items: center;
justify-content: center;
margin: 1.5rem 1.25rem 1.5rem 1.5rem; /* Right margin is the normal width - the border-width */
overflow: visible;
}
/* Setup the rainbow colours on the discs
- #00AFC0
- #00A3E1
- #27579C
- #934FC0
- #C04F8E
- #C04F5D
- #C07E4F
- #C0B54F
*/
.disc-list li:nth-child(1) {
background: #00AFC0;
color: #00AFC0;
}
.disc-list li:nth-child(2) {
background: #00A3E1;
color: #00A3E1;
}
.disc-list li:nth-child(3) {
background: #27579C;
color: #27579C;
}
.disc-list li:nth-child(4) {
background: #934FC0;
color: #934FC0;
}
.disc-list li:nth-child(5) {
background: #C04F8E;
color: #C04F8E;
}
.disc-list li:nth-child(6) {
background: #C04F5D;
color: #C04F5D;
}
.disc-list li:nth-child(7) {
background: #C07E4F;
color: #C07E4F;
}
.disc-list li:nth-child(8) {
background: #C0B54F;
color: #C0B54F;
}
/* The text inside the disc */
.disc-list li span {
color: #FFF;
display: block;
font-weight: bold;
font-size: 0.8rem;
max-width: 5rem;
margin-left: auto;
margin-right: auto;
}
/* The step number */
.disc-list li span::before {
content: counter(step);
display: block;
font-size: 1.65rem;
margin-bottom: 0.1rem;
font-weight: 200;
}
/* The pointer container */
.disc-list li .pointer {
position: absolute;
right: -1.6rem;
width: 1rem;
height: 1rem;
}
/* The arrow-head itself */
.disc-list li .pointer path {
fill: none;
stroke-linejoin: round;
stroke-width: 3px;
stroke: currentcolor; /* This allows the outline to inherit the text color automatically */
}
/* Move the arrow head into position depending if it's an odd/even disc */
.disc-list li:nth-child(even) .pointer {
bottom: 20%;
transform: rotate(22.5deg);
}
.disc-list li:nth-child(odd) .pointer {
top: 20%;
transform: rotate(22.5deg);
}
.disc-list li::before,
.disc-list li::after {
content: '';
position: absolute;
border: 0.25rem solid;
left: -1.5rem; /* Same as the disc-list li margin */
right: -1.5rem;
}
/* The dotted elements */
.disc-list li:nth-child(even)::before {
border-top-style: dotted;
border-left-style: dotted;
border-radius: 50%/100% 100% 0 0;
border-bottom-color: transparent;
top: -1.5rem;
bottom: 50%;
}
.disc-list li:nth-child(odd)::before {
border-bottom-style: dotted;
border-right-style: dotted;
border-top-color: transparent;
border-radius: 50%/0 0 100% 100%;
bottom: -1.5rem;
top: 50%;
}
/* The solid line elements */
.disc-list li:nth-child(odd)::after {
transform-origin: bottom center;
transform: rotate(22.5deg);
border-width: 0.2rem;
border-style: solid;
border-color: transparent;
border-top-color: inherit;
border-left-color: inherit;
border-radius: 50%/100% 100% 0 0;
border-bottom-color: transparent;
top: -1.5rem;
bottom: 50%;
}
.disc-list li:nth-child(even)::after {
transform-origin: top center;
transform: rotate(22.5deg);
border-style: solid;
border-width: 0.2rem;
border-color: transparent;
border-bottom-color: inherit;
border-right-color: inherit;
border-radius: 50%/0 0 100% 100%;
bottom: -1.5rem;
top: 50%;
}
<ol class="disc-list">
<li>
<span>Login to Workday</span>
<svg class="pointer">
<path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z" />
</svg>
</li>
<li>
<span>Upload to Receipts</span>
<svg class="pointer">
<path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z" />
</svg>
</li>
<li>
<span>Submit Report</span>
<svg class="pointer">
<path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z" />
</svg>
</li>
<li>
<span>Get Reimbursed</span>
<svg class="pointer">
<path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
</svg>
</li>
<li>
<span>Do a Dance</span>
<svg class="pointer">
<path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
</svg>
</li>
<li>
<span>Make Sandwiches</span>
<svg class="pointer">
<path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
</svg>
</li>
<li>
<span>Take a nap</span>
<svg class="pointer">
<path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
</svg>
</li>
<li>
<span>Go again!</span>
<svg class="pointer">
<path d="M6.25,1.25L11.25,11.25L1.25,11.25L6.25,1.25Z"/>
</svg>
</li>
</ol>
I'm sure you can fine-tune this further, and optimise the CSS to make it neater - this should give a decent starting point, though. Feel free to ask for more detail if required!
SVG version
This version uses inline SVGs for the decorative elements, rather than border styles. This should give a more consistent result and allow you to fine tune things like the dashes. I've re-written this with SASS for brevity, which SO's code snippet doesn't support, so this is on CodePen: https://codepen.io/companionstudio/pen/BaWqBmm
This doesn't change the wrapping/scaling requirement yet, though I would highly recommend a solution based on HTML like this if you need wrapping.
The canvas draws OK at viewport dimensions 1920 x 1080.
This snippet (which is vanilla JS for demo purposes) draws the canvas as given in the code in the question and then scales it and its position to fit the current viewport.
let arrowColorArr = ["red", "green", "blue", "grey"];
const dia = 170;
const top_position = 180;
const left_position = 180;
function OnInit() {
// for vanilla JS demo only we need to set up the values
this.arrowColorArr = arrowColorArr;
this.dia = dia;
this.top_position = top_position;
this.left_position = left_position;
this.c = document.getElementById('myCanvas');
//ADDED - RESTORE CANVAS TO INITIAL SCALE
c.style.transform = "scale(1)";
this.ctx = this.c.getContext("2d");
this.ctx.clearRect(0, 0, 1920, 600); //ADDED
this.ctx.setLineDash([0.5, 15]);
this.ctx.lineWidth = 7;
this.ctx.lineCap = "round";
//1st down
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.arc(this.left_position, this.top_position + 4, this.dia, 0, 1 * Math.PI);
this.ctx.stroke();
//second circle up
this.ctx.strokeStyle = "#0091da";
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 3.1, 2 * Math.PI);
this.ctx.stroke();
//3rd circle down
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 0, 1 * Math.PI);
this.ctx.stroke();
//4th circle up
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 3.15, 1.98 * Math.PI);
this.ctx.stroke();
this.ctx.lineWidth = 5;
//1st up
this.ctx.setLineDash([0, 0]);
this.ctx.strokeStyle = this.arrowColorArr[0];
this.ctx.beginPath();
this.ctx.arc(this.left_position, this.top_position, this.dia, 3.3, 1.8 * Math.PI);
this.ctx.stroke();
//2nd down
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 0.5, 0.9 * Math.PI);
this.ctx.stroke();
//3rd up
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 3.5, 1.8 * Math.PI);
this.ctx.stroke();
//4th down
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 0.1, 0.9 * Math.PI);
this.ctx.stroke()
//arrow mark
this.ctx.strokeStyle = this.arrowColorArr[0];
this.ctx.beginPath();
this.ctx.moveTo(330, 92);
this.ctx.lineTo(327, 79);
this.ctx.lineTo(317, 89);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.moveTo(677, 248);
this.ctx.lineTo(678, 262);
this.ctx.lineTo(667, 256);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.moveTo(1011, 90);
this.ctx.lineTo(1007, 77);
this.ctx.lineTo(997, 87);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.moveTo(1370, 183);
this.ctx.lineTo(1375, 193);
this.ctx.lineTo(1365, 193);
this.ctx.closePath();
this.ctx.stroke();
//ADDED SO CANVAS SCALES TO WINDOW WIDTH
const scale = window.innerWidth/1920;// 1920 because it draws the canvas OK at that viewport width
c.style.transform = "scale(" + scale + ")";
c.style.top = this.top_position * scale + "px";
c.style.left = this.left_position * scale + "px";
}
window.onload = OnInit;
window.onresize = OnInit;
* {
margin: 0;
padding: 0;
}
canvas {
position: relative;
transform-origin: 0 0;
}
<canvas id="myCanvas" width="1500" height="600"></canvas><!-- was 2000 wide, reduced so doesn't cause X overflow when scaled -->
With these changes inserted your canvas drawing code therefore becomes:
<canvas id="myCanvas" width="1500" height="600"></canvas><!-- was 2000 wide, reduced so doesn't cause X overflow when scaled -->
TS code:
import { Component, Input, OnInit } from '#angular/core';
import { Router } from '#angular/router';
#Component({
selector: 'app-process-card',
templateUrl: './process-card.component.html',
styleUrls: ['./process-card.component.scss']
})
export class ProcessCardComponent implements OnInit {
constructor(public router: Router) { }
#Input()
public data;
public arrowColorArr = [];
public dia = 170;
public top_position = 180;
public left_position = 180;
public c;
public ctx;
public canvasDiv;
public canvasDivMargin = 36;
ngOnInit() {
this.arrowColorArr = this.data.cards.map((cardIn) => {
var eachBgColor = cardIn.bgStyle.background.includes("linear-gradient") ? cardIn.bgStyle.background.substring(35, 43) : cardIn.bgStyle.background;
return eachBgColor.replace(/ /g, '')
})
this.c = document.getElementById('myCanvas');
//ADDED - RESTORE CANVAS TO INITIAL SCALE
c.style.transform = "scale(1)";
this.ctx = this.c.getContext("2d");
// ADDED TO CLEAR THE CANVAS EACH TIME (ON A RESIZE E.G.)
this.ctx.clearRect(0, 0, 1500, 600);
this.ctx.setLineDash([0.5, 15]);
this.ctx.lineWidth = 7;
this.ctx.lineCap = "round";
//1st down
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.arc(this.left_position, this.top_position + 4, this.dia, 0, 1 * Math.PI);
this.ctx.stroke();
//second circle up
this.ctx.strokeStyle = "#0091da";
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 3.1, 2 * Math.PI);
this.ctx.stroke();
//3rd circle down
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 0, 1 * Math.PI);
this.ctx.stroke();
//4th circle up
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 3.15, 1.98 * Math.PI);
this.ctx.stroke();
this.ctx.lineWidth = 5;
//1st up
this.ctx.setLineDash([0, 0]);
this.ctx.strokeStyle = this.arrowColorArr[0];
this.ctx.beginPath();
this.ctx.arc(this.left_position, this.top_position, this.dia, 3.3, 1.8 * Math.PI);
this.ctx.stroke();
//2nd down
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 2), this.top_position, this.dia, 0.5, 0.9 * Math.PI);
this.ctx.stroke();
//3rd up
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 4), this.top_position, this.dia, 3.5, 1.8 * Math.PI);
this.ctx.stroke();
//4th down
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.arc(this.left_position + (this.dia * 6), this.top_position, this.dia, 0.1, 0.9 * Math.PI);
this.ctx.stroke()
//arrow mark
this.ctx.strokeStyle = this.arrowColorArr[0];
this.ctx.beginPath();
this.ctx.moveTo(330, 92);
this.ctx.lineTo(327, 79);
this.ctx.lineTo(317, 89);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[1];
this.ctx.beginPath();
this.ctx.moveTo(677, 248);
this.ctx.lineTo(678, 262);
this.ctx.lineTo(667, 256);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[2];
this.ctx.beginPath();
this.ctx.moveTo(1011, 90);
this.ctx.lineTo(1007, 77);
this.ctx.lineTo(997, 87);
this.ctx.closePath();
this.ctx.stroke();
this.ctx.strokeStyle = this.arrowColorArr[3];
this.ctx.beginPath();
this.ctx.moveTo(1370, 183);
this.ctx.lineTo(1375, 193);
this.ctx.lineTo(1365, 193);
this.ctx.closePath();
this.ctx.stroke();
//ADDED SO CANVAS SCALES TO WINDOW WIDTH
const scale = window.innerWidth/1920;// 1920 because it draws the canvas OK at that viewport width
c.style.transform = "scale(" + scale + ")";
c.style.top = this.top_position * scale + "px";
c.style.left = this.left_position * scale + "px";
}
}
You will similarly need to scale the size and positioning of the HTML elements. Let me know if you'd like help with this.
And remember to add whatever is needed to recalculate the canvas on a resize event.
I have a page with some background image.
In body tag I have a svg element with only one inner path element.
How to add backdrop-filter to path element so it could blur background in non-rectangular shape?
$(function() {
var pattern = "M0,{offsetTop} C{ax1},{power},{ax2},{power},{width},{offsetTop} L{width},{height},0,{height}Z";
var $svg = $('svg#footer');
var $path = $svg.find('path');
var settings = {
width: 1200,
height: 200,
offsetTop: 200,
power: 200
}
settings.ax1 = settings.width / 3 * 1;
settings.ax2 = settings.width / 3 * 2;
function render() {
var newPath = pattern;
for (var i in settings) {
newPath = newPath.split('{' + i + '}').join(settings[i]);
}
$path.attr('d', newPath);
}
TweenMax.set($svg, {
force3D: true
})
var opened = false;
function open() {
if (opened) {
return
}
opened = true;
TweenMax.to(settings, 0.35, {
overwrite: true,
offsetTop: 80,
ease: Strong.easeOut,
onUpdate: render
})
TweenMax.to(settings, 1, {
power: 80,
ease: Elastic.easeOut,
onUpdate: render
})
}
function close() {
if (!opened) {
return
}
opened = false;
TweenMax.to(settings, 0.35, {
overwrite: true,
offsetTop: 200,
ease: Back.easeIn,
onUpdate: render
})
TweenMax.to(settings, 0.35, {
power: 200,
delay: 0.15,
ease: Back.easeOut,
onUpdate: render
})
}
$(window).on('mousedown touchstart', function(e) {
opened ? close() : open();
})
open();
})
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
body {
background-image: url('http://i839.photobucket.com/albums/zz314/mrkanpuc/stuffs/1PZ1.jpg');
background-repeat: no-repeat;
background-size: cover;
}
svg {
position: absolute;
bottom: 0;
width: 100%;
height: 200px;
}
svg path {
fill: rgba(0, 0, 0, 0.5);
}
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<svg id="footer" viewBox="0 0 1200 200" preserveAspectRatio="none"><path/></svg>
Without doing too many changes to your code, you can achieve that by increasing your power and/or decreasing your offsetTop in the open function.
TweenMax.to(settings, 0.35, {overwrite: true, offsetTop: 80, ease: Strong.easeOut, onUpdate: render })
TweenMax.to(settings, 1, {power: 120, ease: Elastic.easeOut, onUpdate: render })
$(function() {
var pattern = "M0,{offsetTop} C{ax1},{power},{ax2},{power},{width},{offsetTop} L{width},{height},0,{height}Z";
var $svg = $('svg#footer');
var $path = $svg.find('path');
var settings = {
width: 1200,
height: 200,
offsetTop: 200,
power: 200
}
settings.ax1 = settings.width / 3 * 1;
settings.ax2 = settings.width / 3 * 2;
function render() {
var newPath = pattern;
for (var i in settings) {
newPath = newPath.split('{' + i + '}').join(settings[i]);
}
$path.attr('d', newPath);
}
TweenMax.set($svg, {
force3D: true
})
var opened = false;
function open() {
if (opened) {
return
}
opened = true;
TweenMax.to(settings, 0.35, {
overwrite: true,
offsetTop: 80,
ease: Strong.easeOut,
onUpdate: render
})
TweenMax.to(settings, 1, {
power: 150,
ease: Elastic.easeOut,
onUpdate: render
})
}
function close() {
if (!opened) {
return
}
opened = false;
TweenMax.to(settings, 0.35, {
overwrite: true,
offsetTop: 200,
ease: Back.easeIn,
onUpdate: render
})
TweenMax.to(settings, 0.35, {
power: 200,
delay: 0.15,
ease: Back.easeOut,
onUpdate: render
})
}
$(window).on('mousedown touchstart', function(e) {
opened ? close() : open();
})
open();
})
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
body {
background-image: url('http://i839.photobucket.com/albums/zz314/mrkanpuc/stuffs/1PZ1.jpg');
background-repeat: no-repeat;
background-size: cover;
}
svg {
position: absolute;
bottom: 0;
width: 100%;
height: 200px;
}
svg path {
fill: rgba(0, 0, 0, 0.5);
}
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<svg id="footer" viewBox="0 0 1200 200" preserveAspectRatio="none"><path/></svg>
Quadratic Bézier curve
Another solution is to add a curved path (called quadratic Bézier curve) to your rectangle. The curve is built like this:
M{startWidth}, {startHeight} q {curvePeak}, {curveHeight}, {endWidth}, {endHeight}
startWidth - x-axis positioning of P0: x coordinate where the curve starts
startHeight - y-axis positioning of P0: y coordinate where the curve starts
curvePeak - x-axis positioning of P1: where the curve reach it's peak
curveHeight - y-axis positioning of P1: height of the curve
endWidth - x-axis positioning of P2: dimension of the curve
endHeight - y-axis positioning of P2: inclination of the curve
See also: Quadratic Bézier Curve: Calculate Points or click here for an interactive example of the quadratic Bézier curve.
Negative
This solution has some problems when using two different animations and duration, like in your case.
Strong.easeOut : 0.35s
Elastic.easeOut : 1.00s
$(function() {
var pattern = "M0,{offsetTop} C{ax1},{power},{ax2},{power},{width},{offsetTop} L{width},{height},0,{height}Z q 600, 100, 1200, 0";
var $svg = $('svg#footer');
var $path = $svg.find('path');
var settings = {
width: 1200,
height: 200,
offsetTop: 200,
power: 200
}
settings.ax1 = settings.width / 3 * 1;
settings.ax2 = settings.width / 3 * 2;
function render() {
var newPath = pattern;
for (var i in settings) {
newPath = newPath.split('{' + i + '}').join(settings[i]);
}
$path.attr('d', newPath);
}
TweenMax.set($svg, {
force3D: true
})
var opened = false;
function open() {
if (opened) {
return
}
opened = true;
TweenMax.to(settings, 0.35, {
overwrite: true,
offsetTop: 80,
ease: Strong.easeOut,
onUpdate: render
})
TweenMax.to(settings, 1, {
power: 80,
ease: Elastic.easeOut,
onUpdate: render
})
}
function close() {
if (!opened) {
return
}
opened = false;
TweenMax.to(settings, 0.35, {
overwrite: true,
offsetTop: 200,
ease: Back.easeIn,
onUpdate: render
})
TweenMax.to(settings, 0.35, {
power: 200,
delay: 0.15,
ease: Back.easeOut,
onUpdate: render
})
}
$(window).on('mousedown touchstart', function(e) {
opened ? close() : open();
})
open();
})
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
body {
background-image: url('http://i839.photobucket.com/albums/zz314/mrkanpuc/stuffs/1PZ1.jpg');
background-repeat: no-repeat;
background-size: cover;
}
svg {
position: absolute;
bottom: 0;
width: 100%;
height: 200px;
}
svg path {
fill: rgba(0, 0, 0, 0.5);
}
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<svg id="footer" viewBox="0 0 1200 200" preserveAspectRatio="none"><path/></svg>
Positive
On the contrary it works great when using the same animation and duration.
Both with Elastic.easeOut : 1.00s
$(function() {
var pattern = "M0,{offsetTop} C{ax1},{power},{ax2},{power},{width},{offsetTop} L{width},{height},0,{height}Z q 600, 100, 1200, 0";
var $svg = $('svg#footer');
var $path = $svg.find('path');
var settings = {
width: 1200,
height: 200,
offsetTop: 200,
power: 200
}
settings.ax1 = settings.width / 3 * 1;
settings.ax2 = settings.width / 3 * 2;
function render() {
var newPath = pattern;
for (var i in settings) {
newPath = newPath.split('{' + i + '}').join(settings[i]);
}
$path.attr('d', newPath);
}
TweenMax.set($svg, {
force3D: true
})
var opened = false;
function open() {
if (opened) {
return
}
opened = true;
TweenMax.to(settings, 1, {
overwrite: true,
offsetTop: 80,
ease: Elastic.easeOut,
onUpdate: render
})
TweenMax.to(settings, 1, {
power: 80,
ease: Elastic.easeOut,
onUpdate: render
})
}
function close() {
if (!opened) {
return
}
opened = false;
TweenMax.to(settings, 0.35, {
overwrite: true,
offsetTop: 200,
ease: Back.easeIn,
onUpdate: render
})
TweenMax.to(settings, 0.35, {
power: 200,
ease: Back.easeIn,
onUpdate: render
})
}
$(window).on('mousedown touchstart', function(e) {
opened ? close() : open();
})
open();
})
html,
body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
body {
background-image: url('http://i839.photobucket.com/albums/zz314/mrkanpuc/stuffs/1PZ1.jpg');
background-repeat: no-repeat;
background-size: cover;
}
svg {
position: absolute;
bottom: 0;
width: 100%;
height: 200px;
}
svg path {
fill: rgba(0, 0, 0, 0.5);
}
<script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.18.0/TweenMax.min.js"></script>
<svg id="footer" viewBox="0 0 1200 200" preserveAspectRatio="none"><path/></svg>
I really like this element,
but how to create it? I am not sure what's the correct designation of the element...
Thank you very much.
This effect can be achieved by layering a couple arc()s:
// bright blue full circle
d.beginPath();
d.arc(50, 50, 50, 0, 2 * Math.PI, false);
d.fillStyle = "#aaeeff";
d.fill();
// dark blue percentage circle
d.beginPath();
d.moveTo(50, 50);
d.arc(50, 50, 50, -0.5 * Math.PI, 0.78 * 2 * Math.PI - 0.5 * Math.PI, false);
d.fillStyle = "#00aaff";
d.fill();
// white inner filler
d.beginPath();
d.moveTo(50, 50);
d.arc(50, 50, 25, 0, 2 * Math.PI, false);
d.fillStyle = "#ffffff";
d.fill();
and finally rendering the text:
d.moveTo(50, 50);
d.fillStyle = "#606060";
d.font = "12pt sans-serif";
d.fillText("78%", 36, 56);
Fiddle: http://jsfiddle.net/j6NVg/
Instead of using the <canvas> element, I have chosen to construct the pie chart relying on CSS and JS entirely. The HTML markup is as follow:
<div class="pie" data-percent="78">
<div class="left">
<span></span>
</div>
<div class="right">
<span></span>
</div>
</div>
The CSS is as follow. The trick is to split the circle into two halves (the nested .left and .right elements). The halves will have their overflowing content hidden, and contain nested <span> that we will manipulate with JS for rotation later. Add vendor prefixes when appropriate :)
.pie {
background-color: #eee;
border-radius: 50%;
width: 200px;
height: 200px;
overflow: hidden;
position: relative;
}
.pie > div {
float: left;
width: 50%;
height: 100%;
position: relative;
overflow: hidden;
}
.pie span {
background-color: #4a7298;
display: block;
width: 100%;
height: 100%;
}
.pie .left span {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
-webkit-transform-origin: 100% 50%;
transform-origin: 100% 50%;
}
.pie .right span {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
-webkit-transform-origin: 0% 50%;
transform-origin: 0% 50%;
}
.pie:before,
.pie:after {
border-radius: 50%;
display: block;
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translateX(-50%) translateY(-50%);
transform: translateX(-50%) translateY(-50%);
}
.pie:before {
background-color: #fff;
content: "";
width: 75%;
height: 75%;
z-index: 100;
}
.pie:after {
content: attr(data-percent) "%";
z-index: 200;
text-align: center;
}
I have used the following with jQuery:
$(function() {
$(".pie").each(function() {
var percent = $(this).data("percent").slice(0,-1), // Removes '%'
$left = $(this).find(".left span"),
$right = $(this).find(".right span"),
deg;
if(percent<=50) {
// Hide left
$left.hide();
// Adjust right
deg = 180 - (percent/100*360)
$right.css({
"transform": "rotateZ(-"+deg+"deg)"
});
} else {
// Adjust left
deg = 180 - ((percent-50)/100*360)
$left.css({
"transform": "rotateZ(-"+deg+"deg)"
});
}
});
});
Here is the fiddle: http://jsfiddle.net/Aw5Rf/7/
Check the below links for more info (not an exact one.But you can get some idea).
<!doctype html>
<html>
<head>
<meta charset="UTF-8" />
<title>Canvas Test</title>
</head>
<body>
<section>
<div>
<canvas id="canvas" width="400" height="300">
This text is displayed if your browser does not support HTML5 Canvas.
</canvas>
</div>
<script type="text/javascript">
var myColor = ["#ECD078","#D95B43","#C02942","#542437","#53777A"];
var myData = [10,30,20,60,40];
function getTotal(){
var myTotal = 0;
for (var j = 0; j < myData.length; j++) {
myTotal += (typeof myData[j] == 'number') ? myData[j] : 0;
}
return myTotal;
}
function plotData() {
var canvas;
var ctx;
var lastend = 0;
var myTotal = getTotal();
canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < myData.length; i++) {
ctx.fillStyle = myColor[i];
ctx.beginPath();
ctx.moveTo(200,150);
ctx.arc(200,150,150,lastend,lastend+
(Math.PI*2*(myData[i]/myTotal)),false);
ctx.lineTo(200,150);
ctx.fill();
lastend += Math.PI*2*(myData[i]/myTotal);
}
}
plotData();
</script>
</section>
</body>
</html>
For more info :Graphing Data in the HTML5 Canvas Element Simple Pie Charts
Another Link : Pure CSS3 Pie Charts effect
This is an online demo: http://jsbin.com/uFaSOwO/1/
First of all what you need can be done exactly using jQuery knob plugin. Still interested in a CSS Solution, than here's what I have done
<div class="load_me"></div>
.load_me {
margin: 100px;
height: 50px;
width: 50px;
border: 5px solid #f00;
border-radius: 50%;
border-top-color: transparent;
}
Demo
Animating the Knob Credits
If you want to prevent the mouse alteration, you can simply add readOnly
$this.knob({
readOnly: true
});
Demo
FIDDLE with ANIMATION
Here's my approach:
var ctx = canvas.getContext('2d');
/*
* in canvas, 0 degrees angle is on the right edge of a circle,
* while we want to start at the top edge of the circle.
* We'll use this variable to compensate the difference.
*/
var relativeAngle = 270;
function drawCanvas() {
ctx.clearRect(0, 0, 90, 90);
//light blue circle
ctx.lineWidth = 20;
ctx.strokeStyle = '#D8E8F7';
ctx.beginPath();
ctx.arc(45, 45, 35, 0, 2*Math.PI);
ctx.stroke();
//dark blue circle
ctx.strokeStyle = '#66ADF4';
ctx.beginPath();
//notice the angle conversion from degrees to radians in the 5th argument
ctx.arc(45, 45, 35, 1.5*Math.PI, ((angle + relativeAngle) / 180) * Math.PI);
ctx.stroke();
//text
ctx.textBaseline = 'middle';
ctx.textAlign = 'center';
ctx.fillStyle = '#666';
ctx.font = 'bold 14px serif';
// angle conversion to percentage value
ctx.fillText(parseInt(100 * angle / 360).toString() + '%', 45, 45);
}
var angle;
function timeout() {
angle = parseInt(360 * percent / 100);
drawCanvas();
if (angle > 360) {
document.getElementById('run').disabled = false;
percent = 0;
return;
}
percent++;
setTimeout(timeout, 10);
};
var percent = 0;
/* START the ANIMATION */
timeout();
At the bottom of the code you'll find a self evaluating function timeout which calls the drawCanvas function every 10 miliseconds and increments the blue circle angle. I hope everything is clear here. If not, feel free to ask!
Enjoy it!
I am a newby so, I wonder if someone can point me in the right direction. I need to draw 3 concentric circles with opacity that need to appear on the screen one after the other. At present, although very faintly, I can see them all. How can I make them appear one after the other?
This is my current code:
<!DOCTYPE HTML>
<html>
<head>
<style>
body {
margin: 0px;
padding: 0px;
background-color: #CCCCCC;
}
</style>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"></head>
<body>
<div id="container"></div>
<script src="kinetic-v4.3.0-beta2.js"></script>
<script>
var fadeIn = function(shape) {
var o = shape.getOpacity();
o = o + 0.05 >=0.5 ? 0.5 : o + 0.05;
shape.setOpacity(o);
shape.getLayer().draw();
if(o !== 0.4) {
setTimeout(function() {
fadeIn(shape).delay(3000*3);
}, 720);
}
};
var stage = new Kinetic.Stage({
container: 'container',
width: 578,
height: 200
});
var layer = new Kinetic.Layer();
var circle = new Kinetic.Circle({
x: stage.getWidth() / 2,
y: stage.getHeight() / 2,
radius: 70,
fill: '#CCCCCC',
stroke: 'yellow',
strokeWidth: 8,
opacity: 0.1
});
setTimeout(function() {
fadeIn(circle).delay(3000*3);
}, 1720);
layer.add(circle);
var circle2 = new Kinetic.Circle({
x: stage.getWidth() / 2.1,
y: stage.getHeight() / 2.1,
radius: 70,
fill: '#CCCCCC',
stroke: 'yellow',
strokeWidth: 8,
opacity: 0.1
});
setTimeout(function() {
fadeIn(circle2).delay(3000*3);
}, 5600);
// add the shape to the layer
layer.add(circle2);
var circle3 = new Kinetic.Circle({
x: stage.getWidth() / 2.2,
y: stage.getHeight() / 2.2,
radius: 70,
fill: '#CCCCCC',
stroke: 'yellow',
strokeWidth: 8,
opacity: 0.1
});
setTimeout(function() {
fadeIn(circle3).delay(3000*3);
}, 12000);
// add the shape to the layer
layer.add(circle3);
// add the layer to the stage
stage.add(layer);
</script>
</body>
</html>
You can hide the circles when you initialize them i.e. circle.hide() before you add them to layer, and then show them when your timeout-callback is called, for instance for circle2, use it like this.
setTimeout(function() {
circle2.show();
fadeIn(circle2).delay(3000*3);
}, 5600);