ArcGIS Api For Javascript Filter Features Combined With Query Features - arcgis-js-api

Iv been having trouble trying to combine the functionality between Filter by attribute and Query Features from a FeatureLayerView. Both samples are below;
https://developers.arcgis.com/javascript/latest/sample-code/featurefilter-attributes/index.html
https://developers.arcgis.com/javascript/latest/sample-code/featurelayerview-query/index.html
The aim is to apply the chosen filter (Filter feature by attribute), and this filter be applied to the Query FeatureLayerView showing in the side panel.
We have currently got both functionality working correctly in one sample, however they are still working in isolation from each other. I have added the code we have below.
Any help would be much appreciated.
<script src="https://js.arcgis.com/4.15/"></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#seasons-filter {
height: 160px;
width: 160px;
width: 100%;
visibility: hidden;
}
.season-item {
width: 100%;
padding: 12px;
text-align: center;
vertical-align: baseline;
cursor: pointer;
height: 40px;
height: 50px;
}
.season-item:focus {
background-color: dimgrey;
}
.season-item:hover {
background-color: dimgrey;
}
.panel-container {
position: relative;
width: 100%;
height: 100%;
}
.panel-side {
padding: 2px;
box-sizing: border-box;
width: 300px;
height: 100%;
position: absolute;
top: 0;
right: 0;
color: #fff;
background-color: rgba(0, 0, 0, 0.6);
overflow: auto;
z-index: 60;
}
.panel-side h3 {
padding: 0 20px;
margin: 20px 0;
}
.panel-side ul {
list-style: none;
margin: 0;
padding: 0;
}
.panel-side li {
list-style: none;
padding: 10px 20px;
}
.panel-result {
cursor: pointer;
margin: 2px 0;
background-color: rgba(0, 0, 0, 0.3);
}
.panel-result:hover,
.panel-result:focus {
color: orange;
background-color: rgba(0, 0, 0, 0.75);
}
</style>
<script>
require([
"esri/views/MapView",
"esri/Map",
"esri/layers/FeatureLayer",
"esri/widgets/Expand"
], function(MapView, Map, FeatureLayer, Expand) {
let floodLayerView;
let graphics;
const listNode = document.getElementById("nyc_graphics");
const popupTemplate = {
// autocasts as new PopupTemplate()
title: "{NAME} in {COUNTY}",
content: [
{
type: "fields",
fieldInfos: [
{
fieldName: "B12001_calc_pctMarriedE",
label: "% Married",
format: {
places: 0,
digitSeparator: true
}
}
]
}
]
};
// flash flood warnings layer
const layer = new FeatureLayer({
url:
"https://services.arcgis.com/P3ePLMYs2RVChkJx/ArcGIS/rest/services/ACS_Marital_Status_Boundaries/FeatureServer/2",
outFields: ["NAME", "GEOID"], // used by queryFeatures
popupTemplate: popupTemplate
});
const map = new Map({
basemap: "gray-vector",
layers: [layer]
});
const view = new MapView({
map: map,
container: "viewDiv",
center: [-73.95, 40.702],
zoom: 11
});
const seasonsNodes = document.querySelectorAll(`.season-item`);
const seasonsElement = document.getElementById("seasons-filter");
// click event handler for seasons choices
seasonsElement.addEventListener("click", filterBySeason);
// User clicked on Winter, Spring, Summer or Fall
// set an attribute filter on flood warnings layer view
// to display the warnings issued in that season
function filterBySeason(event) {
const selectedSeason = event.target.getAttribute("data-season");
floodLayerView.filter = {
where: "State = '" + selectedSeason + "'"
};
}
view.whenLayerView(layer).then(function(layerView) {
// flash flood warnings layer loaded
// get a reference to the flood warnings layerview
floodLayerView = layerView;
// set up UI items
seasonsElement.style.visibility = "visible";
const seasonsExpand = new Expand({
view: view,
content: seasonsElement,
expandIconClass: "esri-icon-filter",
group: "top-left"
});
//clear the filters when user closes the expand widget
seasonsExpand.watch("expanded", function() {
if (!seasonsExpand.expanded) {
floodLayerView.filter = null;
}
});
view.ui.add(seasonsExpand, "top-left");
});
// Start Of Side Bar Element
view.whenLayerView(layer).then(function(layerView) {
layerView.watch("updating", function(value) {
if (!value) {
// wait for the layer view to finish updating
// query all the features available for drawing.
layerView
.queryFeatures({
geometry: view.extent,
returnGeometry: true,
orderByFields: ["NAME"]
})
.then(function(results) {
graphics = results.features;
const fragment = document.createDocumentFragment();
graphics.forEach(function(result, index) {
const attributes = result.attributes;
const name = attributes.NAME;
// Create a list zip codes in NY
const li = document.createElement("li");
li.classList.add("panel-result");
li.tabIndex = 0;
li.setAttribute("data-result-id", index);
li.textContent = name;
fragment.appendChild(li);
});
// Empty the current list
listNode.innerHTML = "";
listNode.appendChild(fragment);
})
.catch(function(error) {
console.error("query failed: ", error);
});
}
});
});
// listen to click event on the zip code list
listNode.addEventListener("click", onListClickHandler);
function onListClickHandler(event) {
const target = event.target;
const resultId = target.getAttribute("data-result-id");
// get the graphic corresponding to the clicked zip code
const result =
resultId && graphics && graphics[parseInt(resultId, 10)];
if (result) {
// open the popup at the centroid of zip code polygon
// and set the popup's features which will populate popup content and title.
view
.goTo(result.geometry.extent.expand(2))
.then(function() {
view.popup.open({
features: [result],
location: result.geometry.centroid
});
})
.catch(function(error) {
if (error.name != "AbortError") {
console.error(error);
}
});
}
}
});
</script>

What you can do is filter the graphics you obtain with the query with a simple condition. Using both examples of ArcGIS, I put together what I think you are trying to get.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="initial-scale=1,maximum-scale=1,user-scalable=no"
/>
<title>Filter and Query - 4.15</title>
<link
rel="stylesheet"
href="https://js.arcgis.com/4.15/esri/themes/light/main.css"
/>
<script src="https://js.arcgis.com/4.15/"></script>
<style>
html,
body,
#viewDiv {
padding: 0;
margin: 0;
height: 100%;
width: 100%;
}
#seasons-filter {
height: 160px;
width: 100%;
visibility: hidden;
}
.season-item {
width: 100%;
padding: 12px;
text-align: center;
vertical-align: baseline;
cursor: pointer;
height: 40px;
}
.season-item:focus {
background-color: dimgrey;
}
.season-item:hover {
background-color: dimgrey;
}
#titleDiv {
padding: 10px;
}
#titleText {
font-size: 20pt;
font-weight: 60;
padding-bottom: 10px;
}
.panel-container {
position: relative;
width: 100%;
height: 100%;
}
.panel-side {
padding: 2px;
box-sizing: border-box;
width: 300px;
height: 100%;
position: absolute;
top: 0;
right: 0;
color: #fff;
background-color: rgba(0, 0, 0, 0.6);
overflow: auto;
z-index: 60;
}
.panel-side h3 {
padding: 0 20px;
margin: 20px 0;
}
.panel-side ul {
list-style: none;
margin: 0;
padding: 0;
}
.panel-side li {
list-style: none;
padding: 10px 20px;
}
.panel-result {
cursor: pointer;
margin: 2px 0;
background-color: rgba(0, 0, 0, 0.3);
}
.panel-result:hover,
.panel-result:focus {
color: orange;
background-color: rgba(0, 0, 0, 0.75);
}
</style>
<script>
require([
"esri/views/MapView",
"esri/Map",
"esri/layers/FeatureLayer",
"esri/widgets/Expand"
], function(MapView, Map, FeatureLayer, Expand) {
const listNode = document.getElementById("list_graphics");
const seasonsNodes = document.querySelectorAll(`.season-item`);
const seasonsElement = document.getElementById("seasons-filter");
let layer, map, view;
let selectedSeason = null;
let floodLayerView;
let graphics = null;
// functions
const filterBySeason = function (event) {
selectedSeason = event.target.getAttribute("data-season");
floodLayerView.filter = {
where: "Season = '" + selectedSeason + "'"
};
updateList();
};
const updateList = function () {
if (!graphics) {
return;
}
const fragment = document.createDocumentFragment();
graphics.forEach(function(result, index) {
const attributes = result.attributes;
if (!selectedSeason || attributes.SEASON === selectedSeason) {
const name = attributes.IssueDate;
// Create the list
const li = document.createElement("li");
li.classList.add("panel-result");
li.tabIndex = 0;
li.setAttribute("data-result-id", index);
li.textContent = name;
fragment.appendChild(li);
}
});
// Empty the current list
listNode.innerHTML = "";
listNode.appendChild(fragment);
};
// flash flood warnings layer
layer = new FeatureLayer({
portalItem: {
id: "f9e348953b3848ec8b69964d5bceae02"
},
outFields: ["SEASON", "IssueDate"]
});
map = new Map({
basemap: "gray-vector",
layers: [layer]
});
view = new MapView({
map: map,
container: "viewDiv",
center: [-98, 40],
zoom: 10
});
// click event handler for seasons choices
seasonsElement.addEventListener("click", filterBySeason);
view.whenLayerView(layer).then(function(layerView) {
/*
filter
*/
floodLayerView = layerView;
// set up UI items
seasonsElement.style.visibility = "visible";
const seasonsExpand = new Expand({
view: view,
content: seasonsElement,
expandIconClass: "esri-icon-filter",
group: "top-left"
});
//clear the filters when user closes the expand widget
seasonsExpand.watch("expanded", function() {
if (!seasonsExpand.expanded) {
floodLayerView.filter = null;
}
});
view.ui.add(seasonsExpand, "top-left");
view.ui.add("titleDiv", "bottom-left");
/*
query
*/
layerView.watch("updating", function(value) {
if (!value) {
// wait for the layer view to finish updating
// query all the features available for drawing.
layerView
.queryFeatures({
geometry: view.extent,
returnGeometry: true,
orderByFields: ["IssueDate"]
})
.then(function (results) {
graphics = results.features;
updateList();
})
.catch(function(error) {
console.error("query failed: ", error);
});
}
});
});
/*
query
*/
// listen to click event on list items
listNode.addEventListener("click", onListClickHandler);
function onListClickHandler(event) {
const target = event.target;
const resultId = target.getAttribute("data-result-id");
// get the graphic corresponding to the clicked item
const result =
resultId && graphics && graphics[parseInt(resultId, 10)];
if (result) {
// open the popup at the centroid of polygon
// and set the popup's features which will populate popup content and title.
view
.goTo(result.geometry.extent.expand(2))
.then(function() {
view.popup.open({
features: [result],
location: result.geometry.centroid
});
})
.catch(function(error) {
if (error.name != "AbortError") {
console.error(error);
}
});
}
};
});
</script>
</head>
<body>
<div class="panel-container">
<div id="seasons-filter" class="esri-widget">
<div class="season-item visible-season" data-season="Winter">Winter</div>
<div class="season-item visible-season" data-season="Spring">Spring</div>
<div class="season-item visible-season" data-season="Summer">Summer</div>
<div class="season-item visible-season" data-season="Fall">Fall</div>
</div>
<div class="panel-side esri-widget">
<ul id="list_graphics">
<li>Loading…</li>
</ul>
</div>
<div id="viewDiv"></div>
<div id="titleDiv" class="esri-widget">
<div id="titleText">Flash Floods by Season</div>
<div>Flash Flood Warnings (2002 - 2012)</div>
</div>
</div>
</body>
</html>
I just move things a bit so that each time the user filter with the seasons the list of the side panel is updated.
Now, you will see that I do not query on a new season, I just filter the graphics that we already have.
Each time a new query is made, the list is going to filter in the same manner.

Im also trying to add an option within the dropdown list to clear or filters and reset the side panel. I have added an on-click event, which clears the filters, but it doesn't reset the side panel.
Any help would be much appreciated.
require([
"esri/views/MapView",
"esri/Map",
"esri/layers/FeatureLayer",
"esri/widgets/Expand"
], function(MapView, Map, FeatureLayer, Expand) {
const listNode = document.getElementById("list_graphics");
const seasonsNodes = document.querySelectorAll(`.season-item`);
const seasonsElement = document.getElementById("seasons-filter");
let layer, map, view;
let selectedSeason = null;
let floodLayerView;
let graphics = null;
// functions
const filterBySeason = function (event) {
selectedSeason = event.target.getAttribute("data-season");
floodLayerView.filter = {
where: "Season = '" + selectedSeason + "'"
};
document
.getElementById("filterReset")
.addEventListener("click", function() {
floodLayerView.filter = selectedSeason;
});
updateList();
};
const updateList = function () {
if (!graphics) {
return;
}
const fragment = document.createDocumentFragment();
graphics.forEach(function(result, index) {
const attributes = result.attributes;
if (!selectedSeason || attributes.SEASON === selectedSeason) {
const name = attributes.IssueDate;
// Create the list
const li = document.createElement("li");
li.classList.add("panel-result");
li.tabIndex = 0;
li.setAttribute("data-result-id", index);
li.textContent = name;
fragment.appendChild(li);
}
});
// Empty the current list
listNode.innerHTML = "";
listNode.appendChild(fragment);
};
// flash flood warnings layer
layer = new FeatureLayer({
portalItem: {
id: "f9e348953b3848ec8b69964d5bceae02"
},
outFields: ["SEASON", "IssueDate"]
});
map = new Map({
basemap: "gray-vector",
layers: [layer]
});
view = new MapView({
map: map,
container: "viewDiv",
center: [-98, 40],
zoom: 10
});
// click event handler for seasons choices
seasonsElement.addEventListener("click", filterBySeason);
view.whenLayerView(layer).then(function(layerView) {
/*
filter
*/
floodLayerView = layerView;
// set up UI items
seasonsElement.style.visibility = "visible";
const seasonsExpand = new Expand({
view: view,
content: seasonsElement,
expandIconClass: "esri-icon-filter",
group: "top-left"
});
//clear the filters when user closes the expand widget
view.ui.add(seasonsExpand, "top-left");
view.ui.add("titleDiv", "bottom-left");
/*
query
*/
layerView.watch("updating", function(value) {
if (!value) {
// wait for the layer view to finish updating
// query all the features available for drawing.
layerView
.queryFeatures({
geometry: view.extent,
returnGeometry: true,
orderByFields: ["IssueDate"]
})
.then(function (results) {
graphics = results.features;
updateList();
})
.catch(function(error) {
console.error("query failed: ", error);
});
}
});
});
/*
query
*/
// listen to click event on list items
listNode.addEventListener("click", onListClickHandler);
function onListClickHandler(event) {
const target = event.target;
const resultId = target.getAttribute("data-result-id");
// get the graphic corresponding to the clicked item
const result =
resultId && graphics && graphics[parseInt(resultId, 10)];
if (result) {
// open the popup at the centroid of polygon
// and set the popup's features which will populate popup content and title.
view
.goTo(result.geometry.extent.expand(2))
.then(function() {
view.popup.open({
features: [result],
location: result.geometry.centroid
});
})
.catch(function(error) {
if (error.name != "AbortError") {
console.error(error);
}
});
}
};
});
</script>```

Related

Using wheelnav.js on Vue 3 project

So I have seen this pie menu generator which gives you an HTML, CSS and JS code and I wanted to use it in my Vue 3 project. I am new to vue and this was how I imported it.
here is the link to the pie menu generator: http://pmg.softwaretailoring.net/
I did this in my wrapper component. Somehow this bought many errors in which I think is because of how I imported the JS library.
<template>
<div class="context-menu" v-show="show" :style="style" ref="context" tabindex="0" #blur="close">
<div id='piemenu' data-wheelnav data-wheelnav-slicepath='DonutSlice' data-wheelnav-marker
data-wheelnav-markerpath='PieLineMarker' data-wheelnav-rotateoff data-wheelnav-navangle='270'
data-wheelnav-cssmode data-wheelnav-init>
<div data-wheelnav-navitemtext='0' onmouseup='alert("Place your logic here.");'></div>
<div data-wheelnav-navitemtext='1' onmouseup='alert("Place your logic here.");'></div>
</div>
</div>
</template>
<script>
import '../assets/raphael.min.js'
import '../assets/raphael.icons.min.js'
import '../assets/wheelnav.min.js'
var piemenu = new wheelnav('piemenu');
piemenu.wheelRadius = piemenu.wheelRadius * 0.83;
piemenu.createWheel();
export default {
name: 'CmpContextMenu',
props: {
display: Boolean, // prop detect if we should show context menu
},
data() {
return {
left: 0, // left position
top: 0, // top position
show: false, // affect display of context menu
};
},
computed: {
// get position of context menu
style() {
return {
top: this.top + 'px',
left: this.left + 'px',
};
},
},
methods: {
// closes context menu
close() {
this.show = false;
this.left = 0;
this.top = 0;
this.myTrigger = false;
console.log('trigger false');
},
open(evt) {
this.show = true;
// updates position of context menu
this.left = evt.pageX || evt.clientX;
this.top = evt.pageY || evt.clientY;
},
},
};
</script>
<style>
.context-menu {
position: fixed;
z-index: 999;
cursor: pointer;
}
#piemenu>svg {
width: 100%;
height: 100%;
}
#piemenu {
height: 400px;
width: 400px;
margin: auto;
}
#media (max-width: 400px) {
#piemenu {
height: 300px;
width: 300px;
}
}
[class|=wheelnav-piemenu-slice-basic] {
fill: #497F4C;
stroke: none;
}
[class|=wheelnav-piemenu-slice-selected] {
fill: #497F4C;
stroke: none;
}
[class|=wheelnav-piemenu-slice-hover] {
fill: #497F4C;
stroke: none;
fill-opacity: 0.77;
cursor: pointer;
}
[class|=wheelnav-piemenu-title-basic] {
fill: #333;
stroke: none;
}
[class|=wheelnav-piemenu-title-selected] {
fill: #fff;
stroke: none;
}
[class|=wheelnav-piemenu-title-hover] {
fill: #222;
stroke: none;
cursor: pointer;
}
[class|=wheelnav-piemenu-title]>tspan {
font-family: Impact, Charcoal, sans-serif;
font-size: 24px;
}
.wheelnav-piemenu-marker {
stroke: #444;
stroke-width: 2;
}
</style>

SvelteKit w/ MSAL.js redirecting automatically for login page

I have an application which is running SvelteKit and MSAL.js, nothing else. I have created a component file for my MSAL instance, you can see that below:
<script context="module">
import * as msal from "#azure/msal-browser";
let signInType = "redirect";
export let accountId = "";
const loginRequest = {
scopes: ["User.Read"],
cache: msal.CacheLookupPolicy.Default,
};
const msalConfig = {
auth: {
clientId: "<client-id>",
authority:
"https://login.microsoftonline.com/<tenant-id>",
redirectUri: "http://localhost:5173/profile",
navigateToLoginRequestUrl: true,
clientSecret: "<secret>",
},
cache: {
cacheLocation: "sessionStorage", // This configures where your cache will be stored
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
},
};
export const currentUser = msalConfig.accountId;
const msalInstance = new msal.PublicClientApplication(msalConfig);
console.dir(msalInstance);
msalInstance
.handleRedirectPromise()
.then(handleResponse)
.catch((err) => {
console.error(err);
});
function handleResponse(resp) {
if (resp !== null) {
accountId = resp.account.homeAccountId;
msalInstance.setActiveAccount(resp.account);
} else {
// need to call getAccount here?
const currentAccounts = msalInstance.getAllAccounts();
if (!currentAccounts || currentAccounts.length < 1) {
return;
} else if (currentAccounts.length > 1) {
// Add choose account code here
} else if (currentAccounts.length === 1) {
const activeAccount = currentAccounts[0];
msalInstance.setActiveAccount(activeAccount);
accountId = activeAccount.homeAccountId;
}
}
}
export async function signIn(method) {
if (signInType === "popup") {
return msalInstance
.loginPopup(loginRequest)
.then(handleResponse)
.catch(function (error) {
console.log(error);
});
} else if (signInType === "redirect") {
return msalInstance.loginRedirect(loginRequest);
}
}
export async function signOut(interactionType) {
const logoutRequest = {
account: msalInstance.getAccountByHomeId(accountId),
};
if (interactionType === "popup") {
msalInstance.logoutPopup(logoutRequest).then(() => {
window.location.reload();
});
} else {
msalInstance.logoutRedirect(logoutRequest);
}
}
</script>
Then I exported my signIn function to my login page, and I created my login button succesfully, using the function from my MSAL instance. The first thing I want to create in my profile page, is the logout but whenever I export any function or variable from MSAL instance to profile page, when the profile page is loaded, it just redirect automatically to my http://localhost:5173, and doesn't stay in http://localhost:5173/profile page.
This is my http://localhost:5173 page:
<script lang="ts">
import MSAL, { signIn } from "../lib/Components/MSAL.svelte";
</script>
<div class="mainContainer">
<div class="title">
<h1>Welcome!</h1>
</div>
<div class="btns">
<button on:click={signIn}>Login</button>
</div>
</div>
<style>
* {
margin: 0;
padding: 0;
}
.mainContainer {
background: url(<link to my image>)
no-repeat center center/cover;
z-index: 1;
box-shadow: inset 50px 60px 250px #ffffff,
inset -50px -60px 120px #ffffff;
height: 100vh;
}
.title {
text-align: center;
padding-top: 7rem;
}
.title h1 {
font-size: 100px;
color: rgba(130, 255, 255, 0.658);
}
.btns {
display: flex;
justify-content: center;
margin-top: 10rem;
}
button {
border: 1px black solid;
border-radius: 15px;
width: 100px;
height: 40px;
}
.btns :hover {
background-color: rgba(130, 255, 255, 0.664);
color: white;
}
</style>
And this is my profile page:
<script lang="ts">
import TopAppBar from "../../lib/Components/TopAppBar.svelte";
import BottomAppBar from "../../lib/Components/BottomAppBar.svelte";
import MSAL, { signOut } from "../../lib/Components/MSAL.svelte";
</script>
<div class="topBar">
<TopAppBar />
</div>
<div>
<button on:click={signOut}>Logout</button>
</div>
<div class="bottomBar">
<BottomAppBar />
</div>
<style>
* {
margin: 0;
padding: 0;
}
.bottomBar {
position: fixed;
z-index: 100;
bottom: 0;
left: 0;
width: 100%;
}
</style>
When I inspect the page, there isn't any error.
Do you guys have any clue why this is happening?
I tried to export my function in different ways but always the same problem. Also added
<script context="module">
But nothing has changed.
I can't do much because there is no error. I'm just expecting that when I login to my system, it doesn't back to my http://localhost:5173, and stays in http://localhost:5173/profile

Reinitialize matterjs after page changes in nuxtjs

I have a matterjs instance in my nuxt app that drops items on the floor. Everything works when I visit the page for the first time or do a page refresh. But when I change the pages (routes) inside my app, so I come back to the page with the matterjs instance, the instance is gone. I always have to do a page refresh...
How can I reinitialize matterjs?
Fallbox
<section class="fallbox">
<div class="fallbox-content">
<nuxt-link to="/"><h1>Index</h1></nuxt-link>
</div>
<div class="fallbox-scene">
<div v-for="item in items" :key="item.className">
<span :class="item.className" class="item"></span>
</div>
</div>
</section>
export default {
data() {
return {
items: [
{
className: "-i1",
},
{
className: "-i2",
},
{
className: "-i3",
},
{
className: "-i4",
},
{
className: "-i5",
},
],
};
},
mounted() {
window.addEventListener("DOMContentLoaded", () => {
this.startFallbox();
});
},
methods: {
startFallbox() {
const Engine = Matter.Engine;
const Render = Matter.Render;
const Runner = Matter.Runner;
const Bodies = Matter.Bodies;
const Body = Matter.Body;
const Composite = Matter.Composite;
const MouseConstraint = Matter.MouseConstraint;
const engine = Engine.create();
const world = engine.world;
engine.gravity.y = 1;
const fallbox = document.querySelector(".fallbox-scene");
const render = Render.create({
element: fallbox,
engine,
options: {
width: fallbox.offsetWidth,
height: fallbox.offsetHeight,
},
});
// Render.run(render);
const runner = Runner.create();
Runner.run(runner, engine);
const itemArray = this.items;
itemArray.forEach((i) => {
const get = document.getElementsByClassName(i.className)[0];
get.style.opacity = 1;
const item = {
w: get.clientWidth,
h: get.clientHeight,
body: Bodies.rectangle(
Math.random() * window.innerWidth,
Math.random() * -1000,
get.clientWidth,
get.clientHeight,
{
restitution: 0.5,
angle: Math.random() * 360,
}
),
elem: get,
render() {
const { x, y } = this.body.position;
this.elem.style.top = `${y - this.h / 2}px`;
this.elem.style.left = `${x - this.w / 2}px`;
this.elem.style.transform = `rotate(${this.body.angle}rad)`;
},
};
Body.rotate(item.body, Math.random() * 360);
Composite.add(world, [item.body]);
(function rerender() {
item.render();
requestAnimationFrame(rerender);
})();
});
const ground = Bodies.rectangle(
fallbox.offsetWidth / 2,
fallbox.offsetHeight,
2000,
1,
{
isStatic: true,
}
);
const left = Bodies.rectangle(
0,
fallbox.offsetHeight / 2,
1,
fallbox.offsetHeight,
{
isStatic: true,
}
);
const right = Bodies.rectangle(
fallbox.offsetWidth,
fallbox.offsetHeight / 2,
1,
fallbox.offsetHeight,
{
isStatic: true,
}
);
Composite.add(world, [ground, left, right]);
const mouseConstraint = MouseConstraint.create(engine, {
element: fallbox,
constraint: {
stiffness: 0.2,
},
});
mouseConstraint.mouse.element.removeEventListener(
"mousewheel",
mouseConstraint.mouse.mousewheel
);
mouseConstraint.mouse.element.removeEventListener(
"DOMMouseScroll",
mouseConstraint.mouse.mousewheel
);
Composite.add(world, mouseConstraint);
Render.lookAt(render, {
min: { x: 0, y: 0 },
max: { x: fallbox.offsetWidth, y: fallbox.offsetHeight },
});
},
},
};
.fallbox {
position: relative;
height: 100vh;
width: 100%;
margin: auto;
background: black;
overflow: hidden;
.fallbox-content {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 95%;
max-width: 1050px;
z-index: 2;
// -khtml-user-select: none;
// -moz-user-select: none;
// -ms-user-select: none;
// user-select: none;
// pointer-events: none;
h1 {
font-size: 160px;
font-weight: 500;
line-height: 140px;
margin-bottom: 150px;
text-align: center;
color: white;
}
}
.fallbox-scene {
height: 100%;
width: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1;
contain: strict;
.item {
height: 120px;
width: 120px;
background: red;
position: absolute;
opacity: 0;
user-select: none;
will-change: transform;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
}
}
}
activated() {
this.startFallbox()
}
Use the activated hook to restart the animation. The code when mounted() is still needed.
I assume the problem is that when you navigate away and you some back the initial animation is finished, but as Nuxt caches some pages they are served from cache and therefore the mounted() hook is not called. This is where the activated() hooks comes in. It's called when a page/component was keept alive and is reactivated.

Vue 3 Composition API component doesn't work reactivate class

I have a button and I just want it animate on a click - make as it been pressed. It works if I make optional component as below:
<template>
<button class="button" :class="{'shadow__click': classButton}" #click="buttonClass">
Tell me already!
</button>
</template>
<script>
export default {
data(){
return {
classButton : false
}
},
methods: {
buttonClass(){
this.classButton = true;
setTimeout(() => {
this.classButton = false
}, 1000)
}
}
}
</script>
<style lang="less">
.button{
cursor: pointer;
padding: 12px 24px;
position: relative;
border-radius: 5px;
background-color: #38b2ac;
border: none;
color: #fff8e1;
}
.shadow__click{
animation: click 1s
}
#keyframes click {
0% {
top: 0;
left: 0;
}
25% {
top: 5px;
left: 1px;
}
50% {
top: 10px;
left: 3px;
}
75% {
top: 5px;
left: 1px;
}
100% {
top: 0;
left: 0;
}
}
</style>
but it doesn't want to work when I do Composition way and I don't see a problem but it simply doesn't work (( I console.loged function and it goes to function changes the value of a variable, but class is not applying. Is it a Vue 3 bug?
<script>
import { ref } from "vue"
setup(){
let classButton = ref(false);
function buttonClass(){
classButton = true;
setTimeout(() => {
classButton = false
}, 1000)
}
return { classButton, buttonClass}
}
</script>
You should mutate the ref using the value field :
let classButton = ref(false);
function buttonClass(){
classButton.value = true;
setTimeout(() => {
classButton.value = false
}, 1000)
}

Show and hide node info on mouseover in cytoscape

I am working on a cytoscape.js graph in browser. I want to show some information of nodes (e.g. node label) as the mouse hovers over the nodes in a cytoscape graph. The following code is working for console.log() but I want to show the information in the browser:
cy.on('mouseover', 'node', function(evt){
var node = evt.target;
console.log( 'mouse on node' + node.data('label') );
});
Please help !
Cytoscape.js has popper.js with tippy. I can give you a working exapmle for popper.js:
popper with tippy:
document.addEventListener("DOMContentLoaded", function() {
var cy = (window.cy = cytoscape({
container: document.getElementById("cy"),
style: [{
selector: "node",
style: {
content: "data(id)"
}
},
{
selector: "edge",
style: {
"curve-style": "bezier",
"target-arrow-shape": "triangle"
}
}
],
elements: {
nodes: [{
data: {
id: "a"
}
}, {
data: {
id: "b"
}
}],
edges: [{
data: {
id: "ab",
source: "a",
target: "b"
}
}]
},
layout: {
name: "grid"
}
}));
function makePopper(ele) {
let ref = ele.popperRef(); // used only for positioning
ele.tippy = tippy(ref, { // tippy options:
content: () => {
let content = document.createElement('div');
content.innerHTML = ele.id();
return content;
},
trigger: 'manual' // probably want manual mode
});
}
cy.ready(function() {
cy.elements().forEach(function(ele) {
makePopper(ele);
});
});
cy.elements().unbind('mouseover');
cy.elements().bind('mouseover', (event) => event.target.tippy.show());
cy.elements().unbind('mouseout');
cy.elements().bind('mouseout', (event) => event.target.tippy.hide());
});
body {
font-family: helvetica neue, helvetica, liberation sans, arial, sans-serif;
font-size: 14px
}
#cy {
position: absolute;
left: 0;
top: 0;
bottom: 0;
right: 0;
z-index: 1;
}
h1 {
opacity: 0.5;
font-size: 1em;
font-weight: bold;
}
<head>
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1, maximum-scale=1">
<script src="https://unpkg.com/cytoscape/dist/cytoscape.min.js"></script>
<script src="https://unpkg.com/popper.js#1.14.7/dist/umd/popper.js"></script>
<script src="https://cdn.jsdelivr.net/npm/cytoscape-popper#1.0.4/cytoscape-popper.min.js"></script>
<script src="https://unpkg.com/tippy.js#4.0.1/umd/index.all.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/tippy.js#4.0.1/index.css" />
</head>
<body>
<div id="cy"></div>
</body>
Hi guys the Stephan's answer won't work using module based code like in React.js so I'm giving you an alternative without tippy
popper.css // popper style
.popper-div { // fill free to modify as you prefer
position: relative;
background-color: #333;
color: #fff;
border-radius: 4px;
font-size: 14px;
line-height: 1.4;
outline: 0;
padding: 5px 9px;
}
App.js
import cytoscape from "cytoscape";
import popper from "cytoscape-popper"; // you have to install it
import "./popper.css";
cytoscape.use(popper);
const cy = cytoscape({....});
cy.elements().unbind("mouseover");
cy.elements().bind("mouseover", (event) => {
event.target.popperRefObj = event.target.popper({
content: () => {
let content = document.createElement("div");
content.classList.add("popper-div");
content.innerHTML = event.target.id();
document.body.appendChild(content);
return content;
},
});
});
cy.elements().unbind("mouseout");
cy.elements().bind("mouseout", (event) => {
if (event.target.popper) {
event.target.popperRefObj.state.elements.popper.remove();
event.target.popperRefObj.destroy();
}
});