I've tried different solutions to animate a sidenav bar rise from bottom right corner. This is the last solution, maybe the best one ( because i'm animating the transform scale() ) in the previous ( width / height ) but the animation still sluggish on the cut corner... with the cut line acting with strange squared forms during the animation ... so i'm looking to solve!
Here the scale() example on glitch
https://glitch.com/edit/#!/heady-grey-lantern
Even on w3schools site the simple animation of Side Navigation is little shimmy and flicker
https://www.w3schools.com/howto/howto_js_sidenav.asp
Here the core of animation
// css code
.sidenav {
width: 200vw;
height: 200vh;
width: 200dvw;
height: 200dvh;
position: fixed;
bottom: 0;
/* left: 0; */
right: 0;
/* cut corner */
--cutting: max(100vw, 100vh);
--cutting: max(150dvw, 150dvh);
clip-path: polygon(0 var(--cutting), var(--cutting) 0,100% 0,100% 100%,0 100%);
/* z-index: 1; */
background-color: #111;
/* overflow-x: hidden; */
will-change: transform;
transform: scale(0);
transform-origin: bottom right;
}
// Js code
const sidenav = document.getElementById("mySidenav")
const duration = 500
const openSidenav = [
{ transform: 'scaleX(0) scaleY(0)' },
{ transform: 'scaleX(1) scaleY(1)' }
]
const closeSidenav = [
{ transform: 'scaleX(1) scaleY(1)' },
{ transform: 'scaleX(0) scaleY(0)' }
]
const openNav = () => {
console.log('#NAV >> Open')
// document.getElementById("mySidenav").style.width = '175vw'
// document.getElementById("mySidenav").style.height = '175vh'
sidenav.animate(
openSidenav, {
duration,
iterations: 1,
easing: 'ease-in',
fill: 'forwards'
}
)
}
const closeNav = () => {
console.log('#NAV >> Close')
// document.getElementById("mySidenav").style.width = '0'
// document.getElementById("mySidenav").style.height = '0'
sidenav.animate(
closeSidenav, {
duration,
iterations: 1,
easing: 'ease-in',
fill: 'forwards'
}
)
}
Last, better one!
/* css */
#sidenav {
width: 100vw;
height: 100vh;
width: 100dvw;
height: 100dvh;
position: fixed;
/* cut corner */
--cutting: max(100vw, 100vh);
clip-path: polygon(0 var(--cutting), var(--cutting) 0,100% 0,100% 100%,0 100%);
/* set transition as in WebAnimation API */
transition: all 0.5s;
will-change: transform;
transform: scale(0);
transform-origin: bottom right;
z-index: 11;
background-color: var(--surface1);
display: grid;
justify-items: center;
align-items: center;
/* justify-items: end;
align-items: end; */
}
#sidenav[active] {
--cutting: 0;
transition: all 0.5s;
}
/* --------------- Mobile Nav -------------- */
nav {
/* TODO fallback inline-size: 75vw;
block-size: 75vh; */
inline-size: 75dvw;
block-size: 75dvh;
display: block;
flex-direction: column;
background-color: crimson;
}
// javascript animations
const openSidenav = [
{ transform: 'scale(0)' },
{ transform: 'scale(0.25)' },
{ transform: 'scale(0.5)' },
{ transform: 'scale(0.75)' },
{ transform: 'scale(1)' }
]
const closeSidenav = [
{ transform: 'scale(1)' },
{ transform: 'scale(0.75)' },
{ transform: 'scale(0.5)' },
{ transform: 'scale(0.25)' },
{ transform: 'scale(0)' }
]
const slideIn = [
{ transform: 'translateX(100%)' },
{ transform: 'translateX(0)', opacity: 1 }
]
const slideOut = [
{ transform: 'translateX(0)' },
{ transform: 'translateX(100%)', opacity: 0 }
]
// duration animation
const openSidenavDuration = 500
const closeSidenavDuration = 300
// duration animation per a tag
const slideInDuration = 200
const slideOutDuration = 100
// javascript
openNav (e) {
const sidenav =
this.renderRoot.getElementById('sidenav')
this.sidenavIsOpen = true
const openAnimation = sidenav.animate(
openSidenav, {
duration: openSidenavDuration,
iterations: 1,
easing: 'ease-in',
fill: 'forwards'
}
)
openAnimation.addEventListener('finish', (e) => {
// console.log('#EVENT >> ', e.type)
sidenav.setAttribute('active', '')
// sidenav.style.setProperty('--cutting', 0)
})
}
closeNav (e) {
const sidenav =
this.renderRoot.getElementById('sidenav')
this.sidenavIsOpen = false
sidenav.animate(
closeSidenav, {
duration: closeSidenavDuration,
iterations: 1,
easing: 'ease-in',
fill: 'forwards',
delay: slideOutDuration * 3
}
)
sidenav.removeAttribute('active')
}
Related
I am using latest version of formsflow.ai and I want to create a custom toggle button. How can I do that in formflow.ai. I refer the following documents of formio to do that.
I am expecting to include a custom toggle button in drag and drop mechanism of formsflow.ai
step 1:create the components inside the forms-flow-web in the following order
step 2:Toggle.js code
import React from "react";
import ReactDOM from "react-dom";
import { ReactComponent } from "react-formio";
import settingsForm from "./Toggle.settingsForm";
import ToggleCustomComp from "./Togglechange";
export default class Toggle extends ReactComponent {
/**
* This function tells the form builder about your component. It's name, icon and what group it should be in.
*
* #returns {{title: string, icon: string, group: string, documentation: string, weight: number, schema: *}}
*/
static get builderInfo() {
return {
title: "Toggle",
icon: "square",
group: "basic",
documentation: "",
weight: 120,
schema: Toggle.schema()
};
}
/**
* This function is the default settings for the component. At a minimum you want to set the type to the registered
* type of your component (i.e. when you call Components.setComponent('type', MyComponent) these types should match.
*
* #param sources
* #returns {*}
*/
static schema() {
return ReactComponent.schema({
type: "toggleCustomComp",
label: "ToggleButton"
});
}
/*
* Defines the settingsForm when editing a component in the builder.
*/
static editForm = settingsForm;
/**
* This function is called when the DIV has been rendered and added to the DOM. You can now instantiate the react component.
*
* #param DOMElement
* #returns ReactInstance
*/
attachReact (element) {
let instance;
return ReactDOM.render(
<ToggleCustomComp
ref={(refer) => {instance = refer;}}
component={this.component} // These are the component settings if you want to use them to render the component.
value={this.datavalue} // The starting value of the component.
onChange={this.updateValue}
data={this.data}
disabled={this.disabled}
// The onChange event to call when the value changes.
/>,
element,() => (this.reactInstance = instance)
);
}
/**
* Automatically detach any react components.
*
* #param element
*/
detachReact(element) {
if (element) {
ReactDOM.unmountComponentAtNode(element);
}
}
}
step 3:Toggle.settingsForm.js
import baseEditForm from 'formiojs/components/_classes/component/Component.form';
const settingsForm = (...extend) => {
return baseEditForm([
{
key: 'display',
components: [
{
// You can ignore existing fields.
key: 'placeholder',
ignore: true,
},
]
},
{
key: 'data',
components: [],
},
{
key: 'validation',
components: [],
},
{
key: 'api',
components: [],
},
{
key: 'conditional',
components: [],
},
{
key: 'logic',
components: [],
},
], ...extend);
}
export default settingsForm;
step 4:Togglechange.jsx
import React, {Component} from 'react';
import '../Toggle/toggle.css';
/**
* An example React component this is simply a controlled input element.
*
*/
export default class ToggleCustomComp extends Component {
constructor(props) {
super(props);
this.state = {
value: props.value
}
}
updateCommentData = (event) => {
//const {type} = this.props.component;
this.setState({value: {checked:event.target.checked}}, () => this.props.onChange(this.state.value));
};
render() {
const {disabled, name} = this.props;
let { value } = this.state;
const checked = value?.checked || false;
return (
/*<input type="text" value={value} className={this.props.component.customClassName} onChange={this.setValue}></input>*/
// <input type="checkbox" id="vehicle3" name="vehicle3" value="Boat"></input>
<label class="switch">
<input
name={name}
value={checked}
type="checkbox"
className="form-control"
onChange={(e)=>this.updateCommentData(e)}
disabled={disabled}
/>
<span class="slider round" />
</label>
);
}
};
step 5 :css file
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
step 6:index.js file
import TextAreaWithAnalytics from "./TextAreaWithAnalytics/TextAreaWithAnalytics";
import Toggle from "./Toggle/Toggle";
const components = {
textAreaWithAnalytics: TextAreaWithAnalytics,
toggleCustomComp: Toggle
};
export default components;
I working on a grafana world map project that can receive lat,lon from client and process it on map
everything works fine, until client gave a same lat,lon location where it already spot in a map the new marker land beneath old marker and so on if client still gave the same lat,lon
I need a marker land to exact location that client gave
I think an issue is about zIndex because every marker have the same zIndex (210) so that all of them
can't land on their own lat,lon but I'm so confused about it and I have try to look at markerClusterGroup but I have no idea about it
Marker that land beneath old marker:
t.prototype.createCircle = function(t) {
L.HtmlIcon = L.Icon.extend({
options: {
/*
html: (String) (required)
iconAnchor: (Point)
popupAnchor: (Point)
*/
},
initialize: function (options) {
L.Util.setOptions(this, options);
},
createIcon: function () {
var div = document.createElement('div');
div.innerHTML = this.options.html;
return div;
},
createShadow: function () {
return null;
}
});
const markerLocation = new L.LatLng(t.locationLatitude, t.locationLongitude);
const HTMLIcon = L.HtmlIcon.extend({
options : {
html : "<div class=\"map__marker\"></div>",
}
});
const customHtmlIcon = new HTMLIcon();
const marker = new L.Marker(markerLocation, {icon: customHtmlIcon});
this.map.addLayer(marker);
return this.createPopup(marker, t.locationName, t.valueRounded), marker
}
I already solve it by change relative to fixed on CSS file:
.map__marker {
background: #ffffff;
border-radius: 10px;
height: 10px;
position: fixed; //change from relative
width: 10px;
}
.map__marker::before {
-webkit-animation: blink 1s infinite ease-out;
animation: blink 1s infinite ease-out;
border-radius: 60px;
box-shadow: inset 0 0 0 1px #ffffff;
content: "";
height: 10px;
left: 50%;
opacity: 1;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
width: 10px;
}
#-webkit-keyframes blink {
100% {
height: 50px;
opacity: 0;
width: 50px;
}
}
#keyframes blink {
100% {
height: 50px;
opacity: 0;
width: 50px;
}
}
I have test where I test drag and drop element. In localhost is everything ok. When I do pull request on github the test falls on the drag and drop movement. Do you have any advice what I can do?I am using this code.
cy.dragAndDrop('[style="width: 843px; height: 230px; position: absolute; transform: translate(10px, 10px); z-index: 0;"] > .rounded-xl > .absolute > .pt-4 > .drag-handle','[style="transform: translate(10px, 490px); width: 843px; height: 230px; position: absolute;"] > .rounded-xl > .flex-col' )
cy.dragAndDrop('[style="transform: translate(10px, 10px); width: 843px; height: 230px; position: absolute; z-index: 0;"] > .rounded-xl > .absolute > .pt-4 > .drag-handle','[style="transform: translate(10px, 250px); width: 843px; height: 230px; position: absolute;"] > .rounded-xl')
here is the commands.ts part:
Cypress.Commands.add('dragAndDrop', (subject, target) => {
Cypress.log({
name: 'DRAGNDROP',
message: `Dragging element ${subject} to ${target}`,
consoleProps: () => {
return {
subject: subject,
target: target
};
}
});
const BUTTON_INDEX = 0;
const SLOPPY_CLICK_THRESHOLD = 10;
cy.get(target)
.first()
.then($target => {
let coordsDrop = $target[0].getBoundingClientRect();
cy.get(subject)
.first()
.then(subject => {
const coordsDrag = subject[0].getBoundingClientRect();
cy.wrap(subject)
.trigger('mousedown', {
button: BUTTON_INDEX,
clientX: coordsDrag.x,
clientY: coordsDrag.y,
force: true
})
.trigger('mousemove', {
button: BUTTON_INDEX,
clientX: coordsDrag.x + SLOPPY_CLICK_THRESHOLD,
clientY: coordsDrag.y,
force: true
});
cy.get('body')
.trigger('mousemove', {
button: BUTTON_INDEX,
clientX: coordsDrop.x,
clientY: coordsDrop.y,
force: true
})
.trigger('mouseup',{force:true});
Does anyone know how I can do transitions in leaflet.js with a circleMarker please?
In older versions (0.7 if I am not mistaken) the following css used to do the trick
.leaflet-clickable {
transition: all .3s;
}
but not anymore. I am using version 1.3.1
Set a custom class on your marker and use it to set your transition. For example:
Using this marker
L.circleMarker([0, 0], {
className: 'circle-transition'
}).addTo(map)
You can have a transition on hover with
.circle-transition:hover {
fill: red;
fill-opacity: 1;
transition: all 1s
}
And a demo
var map = L.map('map').setView([0, 0], 4);
L.circleMarker([0, 0], {
radius: 100,
className: 'circle-transition',
fillOpacity: 0.5
}).addTo(map)
html, body {
height: 100%;
margin: 0;
}
#map {
width: 100%;
height: 100%;
}
#keyframes fadeIn {
from { fill-opacity:0; }
to { fill-opacity:0.5; }
}
.circle-transition {
animation: 1s ease-out 0s 1 fadeIn;
}
.circle-transition:hover {
fill: red;
fill-opacity: 1;
transition: all 1s
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.4/leaflet.css"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.4/leaflet.js"></script>
<div id='map'></div>
I'm trying to make an animation with velocity.js of a map. I tried 2 different libraries: leaflet, and openlayers3.
Here are the jsfiddles: leaflet, openlayers3
The leaflet animation is smooth on my chrome, but not on firefox, edge, or even qt webview.
I know that invalidateSize()/updateSize() just change the position of the tiles and download new ones but I want them to produce a smooth animation.
wish somebody have seen this kind of animation. Or know how could I solve the issue, thanks.
leaflet
$(document).ready(function() {
var position = {
lat: 43.180176,
lng: 13.792964,
zoomLevel: 4
};
var swBound = L.latLng(-90, -180);
var neBound = L.latLng(90, 180);
var maxBounds = L.latLngBounds(swBound, neBound);
var entityMap = L.map($("#smallMapContainer")[0], {
minZoom: 2,
maxBounds: maxBounds,
zoomControl: false
});
var streetMapURL = "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png";
L.tileLayer(streetMapURL, {
maxZoom: 18
}).addTo(entityMap);
//entityMap.fitWorld();
entityMap.setView(L.latLng(position.lat, position.lng), position.zoomLevel);
var nextIndexes = 0;
var aaa = function() {
var smallMapPosition = $("#smallMapContainer").position();
var newW = $("body").width() - 90;
var newH = $("body").height() - 90;
var newX = smallMapPosition.top + newH / 2 - 100;
var newY = smallMapPosition.left + newW / 2 - 150;
$("#smallMapContainer").velocity({
top: newX,
left: newY
}, {
duration: 500,
complete: function() {
$("#smallMapContainer").velocity({
width: newW,
height: newH,
top: smallMapPosition.top,
left: smallMapPosition.left
}, {
duration: 1000,
progress: function() {
entityMap.invalidateSize();
},
complete: function() {
if (nextIndexes++ % 2 == 0) { // with animation
entityMap.setView(L.latLng(55.751674, 37.637059), position.zoomLevel);
} else {
entityMap.setView(L.latLng(43.180176, 13.792964), position.zoomLevel);
}
$("#smallMapContainer").velocity({
width: 300,
height: 200,
top: newX,
left: newY
}, {
delay: 1000,
duration: 1000,
progress: function() {
entityMap.invalidateSize();
},
complete: function() {
$("#smallMapContainer").velocity({
top: smallMapPosition.top,
left: smallMapPosition.left
}, {
duration: 1000
});
}
});
}
});
}
});
}
aaa();
setTimeout(function() {
aaa();
}, 10000);});
openlayers
$(document).ready(function() {
var view = new ol.View({
// the view's initial state
center: ol.proj.fromLonLat([13.792964, 43.180176]),
zoom: 4
});
var map = new ol.Map({
layers: [
new ol.layer.Tile({
preload: 4,
source: new ol.source.OSM()
})
],
loadTilesWhileAnimating: true,
target: 'smallMapContainer',
controls: ol.control.defaults({
attributionOptions: ({
collapsible: false
})
}),
view: view
});
nextIndexes = 0;
var animateMap = function() {
var smallMapPosition = $("#smallMapContainer").position();
var newW = $("body").width() - 90;
var newH = $("body").height() - 90;
var newX = smallMapPosition.top + newH / 2 - 100;
var newY = smallMapPosition.left + newW / 2 - 150;
$("#smallMapContainer").velocity({
top: newX,
left: newY
}, {
duration: 500,
complete: function() {
$("#smallMapContainer").velocity({
width: newW,
height: newH,
top: smallMapPosition.top,
left: smallMapPosition.left
}, {
duration: 1000,
progress: function() {
map.updateSize();
},
complete: function() {
if (nextIndexes++ % 2 == 0) {
var pan = ol.animation.pan({
duration: 1000,
source: /** #type {ol.Coordinate} */ (view.getCenter())
});
map.beforeRender(pan);
view.setCenter(ol.proj.fromLonLat([37.637059, 55.751674]));
} else {
var pan = ol.animation.pan({
duration: 1000,
source: /** #type {ol.Coordinate} */ (view.getCenter())
});
map.beforeRender(pan);
view.setCenter(ol.proj.fromLonLat([13.792964, 43.180176]));
}
$("#smallMapContainer").velocity({
width: 300,
height: 200,
top: newX,
left: newY
}, {
delay: 1000,
duration: 1000,
progress: function() {
map.updateSize();
},
complete: function() {
$("#smallMapContainer").velocity({
top: smallMapPosition.top,
left: smallMapPosition.left
}, {
duration: 1000
});
}
});
}
});
}
});
}
animateMap();});
Disclaimer: If you're adament on using Velocity this is in no means a validate answer but i thought i'de throw it in here because i think it's unnecessary to pull in an entire animation library to accomplish something that can easily be done with standard CSS.
You can accomplish the same effect by using CSS keyframe animation, it will run much smoother than using an external animation library:
#leaflet {
width: 300px;
height: 200px;
position: absolute;
top: 55px;
left: 45px;
animation-name: move;
animation-duration: 5s;
animation-iteration-count: infinite;
animation-direction: normal;
animation-timing-function: linear;
}
#keyframes move {
0% {
top: 55px;
left: 45px;
}
25% {
top: calc(50% - (200px / 2));
left: calc(50% - (300px / 2));
width: 300px;
height: 200px;
}
50% {
top: calc(5%);
left: calc(5%);
width: 90%;
height: 90%;
}
75% {
top: calc(50% - (200px / 2));
left: calc(50% - (300px / 2));
width: 300px;
height: 200px;
}
100% {
top: 55px;
left: 45px;
}
}
The only drawback/problem is that somehow (got to look further into this, but i'm low on time at the moment) L.Map's 'resize' event doesn't seem to fire when resizing the map container through CSS animation. So i've used ResizeSensor from CSS Element Queries to be able to call invalidateSize on the map instance when the container gets resized.
Here's a working example on Plunker: http://plnkr.co/edit/EyQFbm?p=preview