More than 100 points and one polyline, Google Maps v3 - ruby-on-rails

I'm working with google maps api v3 and I'm trying to use snap to road with more than 100 points but in addition end up with just one polyline with the whole route that I can put a small animation. The view is a html.erb.
var apiKey = any_key;
var map = handler.getMap();
var drawingManager;
var placeIdArray = [];
var snappedCoordinates = [];
var path = <%= raw(#locations) %>
var markers = <%= raw(#markers) %>
var centerOn = path[0].split(',');
function breadCrumbsGrapher(path) {
handler.removeMarkers(Gmaps.store.markers);
for(var i = 0; i < polylines.length; i++) {
polylines[i].setMap(null);
}
var divided = handlePath(path);
if (typeof divided[0] == 'object') {
for(var i = 0; i < divided.length; i++) {
runSnapToRoad(divided[i]);
}
} else {
runSnapToRoad(path);
}
}
function waypointsLimiter(path) {
var path_loc_size = path.length;
var limited = [];
if(path_loc_size > 30) {
var stepper = Math.ceil(path_loc_size/30);
for(var i = stepper; i < path_loc_size; i += stepper) {
limited.push(path[i]);
}
if(limited.indexOf(path[path_loc_size-1]) == -1) {
limited.push(path[path_loc_size-1]);
}
} else {
limited = path;
}
return limited;
}
function handlePath(path) {
var i = 0;
var j = path.length;
if (j > 100) {
var newArray = [],
chunk = j/2;
if (j >= 200) {
chunk = j/3;
} else if (j >= 300) {
chunk = j/4;
} else if (j >= 400) {
chunk = j/5;
} else if (j >= 500 ) {
chunk = j/6;
} else if (j >= 600) {
chunk = j/7;
} else if (j >= 700 || j <= 799) {
chunk = j/8;
} else {
alert('La ruta no puede ser mostrada');
}
for (i, j; i < j; i+=chunk) {
newArray.push(path.slice(i,i+chunk+1));
}
return newArray;
} else {
return path;
}
}
// Snap a user-created polyline to roads and draw the snapped path
function runSnapToRoad(path) {
var path = path.join('|');
$.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: path,
}, function(data) {
processSnapToRoadResponse(data);
drawSnappedPolyline();
});
}
// Store snapped polyline returned by the snap-to-road service.
function processSnapToRoadResponse(data) {
snappedCoordinates = [];
placeIdArray = [];
for (var i = 0; i < data.snappedPoints.length; i++) {
var latlng = new google.maps.LatLng(
data.snappedPoints[i].location.latitude,
data.snappedPoints[i].location.longitude);
snappedCoordinates.push(latlng);
placeIdArray.push(data.snappedPoints[i].placeId);
}
}
// Draws the snapped polyline (after processing snap-to-road response).
function drawSnappedPolyline() {
var symbol = {
path: google.maps.SymbolPath.FORWARD_CLOSED_ARROW,
scale: 3,
strokeColor: '#3B16B3'
};
var snappedPolyline = new google.maps.Polyline({
path: snappedCoordinates,
strokeColor: '#E51E25',
strokeWeight: 3,
icons: [{
icon: symbol,
offset: '0%'
}]
});
snappedPolyline.setMap(map);
animate(snappedPolyline);
zoomToObject(snappedPolyline);
polylines.push(snappedPolyline);
}
function zoomToObject(obj){
var bounds = new google.maps.LatLngBounds();
var points = obj.getPath().getArray();
for (var n = 0; n < points.length ; n++){
bounds.extend(points[n]);
}
map.fitBounds(bounds);
}
function animate(line) {
var count = 0;
window.setInterval(function() {
count = (count + 1) % 600;
var icons = line.get('icons');
icons[0].offset = (count / 6) + '%';
line.set('icons', icons);
}, 70);
}
breadCrumbsGrapher(path);
Also I've tried declaring a variable outside so I can concat all of the coordinates and generate a polyline with it but doesn't seem to work. Actualy that big array ends up being of 2000+ points.
The result that I have with the provided code
After all of that the issue is that I don't know how to merge the polylines to have just one line and being able to animate just that one line. If there's more than 100 coordinates I plot more polylines. In the image you can see that there's 3 icons (one for each polyline) and I need just to draw one line and have 1 icon.
To reproduce the issue just add a key and if you want use this set of coordinates:
https://drive.google.com/file/d/1jLb7Djv5DiSdR3k4QZRSatXBwrohlxcI/view?usp=sharing
function breadCrumbsGrapher(path) {
//mapMarkers();
snappedCoordinates = [];
handler.removeMarkers(Gmaps.store.markers);
for(var i = 0; i < polylines.length; i++) {
polylines[i].setMap(null);
}
var divided = handlePath(path);
if (typeof divided[0] == 'object') {
for(var i = 0; i < divided.length; i++) {
runSnapToRoad(divided[i]);
}
} else {
runSnapToRoad(path);
}
console.log(snappedCoordinates);
drawSnappedPolyline();
}
function runSnapToRoad(path) {
var path = path.join('|');
$.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: path,
}, function(data) {
processSnapToRoadResponse(data);
//drawSnappedPolyline();
});
}
I've changed the code but it doesn't work, even though I end up with a 2,557 coordinates array.
I've tried also tried this thinking that this could give me the time to have all coordinates:
async function breadCrumbsGrapher(path) {
//mapMarkers();
snappedCoordinates = [];
handler.removeMarkers(Gmaps.store.markers);
for(var i = 0; i < polylines.length; i++) {
polylines[i].setMap(null);
}
var divided = handlePath(path);
if (typeof divided[0] == 'object') {
for(var i = 0; i < divided.length; i++) {
await runSnapToRoad(divided[i]);
}
} else {
await runSnapToRoad(path);
}
console.log(snappedCoordinates);
drawSnappedPolyline();
}
and:
async function runSnapToRoad(path) {
var path = path.join('|');
await $.get('https://roads.googleapis.com/v1/snapToRoads', {
interpolate: true,
key: apiKey,
path: path,
}, function(data) {
processSnapToRoadResponse(data);
});
}
Thank you in advance.

You are using $.get() to query the Roads API which is an asynchronous call so when you call drawSnappedPolyline() from within your breadCrumbsGrapher function, you most probably don't (yet) have all coordinates in return from your AJAX call(s).
If you have 550 coordinates in your original path, you then know you must call the Roads API 6 times (5 times with 100 points + 1 time with 50 points). You should then be able to set a counter somewhere, to only draw your (complete) Polyline, once you got your 6 responses from the Roads API.
Or you could base that on the length of your final array (compared to the original path) although I don't know what would happen in case some points can't be "snapped".

Related

Dart: Class members seem to be wrong when access from a separate class

Anyone know if this is a Dart bug or is it my misunderstanding of how Dart coding works?
I am learning Dart to investigate feasibility of eventually using Flutter; however, while exploring the language, I found a weird behavior (maybe a bug). I tried repro'ing it by writing a similar pattern of code, but have yet to figure out what causes it. In the attached code, I wrote a quicksort class. In that class, it counts the number of times the "sort" method is recursed and saves the count in a class member called "recurseCount".
From the main() class, if I use the QuickSort class directly, I have no issue getting back the recurseCount member; however, if I call it from a different class (called "Tester"), I do not get the correct value for "recurseCount". Why would calling a class from a separate class cause members to not provide the correct values?
import 'package:test/test.dart';
import 'dart:math' as _math;
// ***********************
enum SortOrder { ascending, descending, unsorted }
class QuickSort {
List list = [];
SortOrder sortOrder = SortOrder.unsorted;
int recurseCount = 0;
QuickSort({this.list}) {
if (list != null && list.length > 1) {
list = sort(useRandomPivot: true);
}
}
List sort(
{List iList,
int leftIndex = 0,
int rightIndex,
bool useRandomPivot = true}) {
if (iList != null) list = iList;
if (list.isEmpty) return [];
rightIndex ??= list.length - 1;
if (rightIndex > list.length - 1) rightIndex = list.length - 1;
if (leftIndex < rightIndex) {
recurseCount++;
var partitionIndex =
_partition(leftIndex, rightIndex, useRandomPivot: useRandomPivot);
if (partitionIndex == -1) {
//already sorted List
if (sortOrder == SortOrder.ascending) {
return list;
} else {
//SortOrder.descending
list = list.reversed.toList(); // Time Complexity of O(n)
sortOrder = SortOrder.ascending;
return list;
}
} else {
sort(leftIndex: leftIndex, rightIndex: partitionIndex - 1);
sort(leftIndex: partitionIndex + 1, rightIndex: rightIndex);
}
} else {
sortOrder = SortOrder.ascending;
}
return list;
}
int _partition(int leftIndex, int rightIndex, {bool useRandomPivot = true}) {
// in case the array is already sorted from the start; only run through the partition'ing one time
// note: regardless of ascending or descending order
if (leftIndex == 0 && rightIndex >= list.length - 1) {
sortOrder = checkSorting(list);
if (sortOrder != SortOrder.unsorted) {
return -1;
}
if (useRandomPivot) {
var random = _math.Random();
var randomIndex = random.nextInt(rightIndex - leftIndex);
_swapElements(randomIndex, rightIndex);
}
}
int pivotVal = list[rightIndex]; //select the last item as the pivot
var headIndex = leftIndex - 1;
for (var scanIndex = leftIndex; scanIndex < rightIndex; scanIndex++) {
if (list[scanIndex] <= pivotVal) {
headIndex++;
_swapElements(headIndex, scanIndex);
}
}
var partitionIndex = headIndex + 1;
_swapElements(partitionIndex, rightIndex);
return partitionIndex;
}
void _swapElements(position1, position2) {
int tempVal = list[position1];
list[position1] = list[position2];
list[position2] = tempVal;
}
SortOrder checkSorting(List arr) {
var isAsc = true;
var isDesc = true;
for (var i = 0; i < arr.length - 1; i++) {
if (arr[i] < arr[i + 1]) isDesc = false;
if (arr[i] > arr[i + 1]) isAsc = false;
if (!isDesc && !isAsc) break; // not sorted Asc or Desc
}
if (isAsc) {
return SortOrder.ascending;
} else if (isDesc) return SortOrder.descending;
return SortOrder.unsorted;
}
}
// ***********************TESTS*********************************
num log2(num n) => _math.log(n) / _math.ln2;
List getRandomIntList({int min = 0, int max = 10000, int len}) {
var random = _math.Random();
var tempList = List(len);
for (var i = 0; i < len; i++) {
tempList[i] = random.nextInt(max - min);
}
return tempList;
}
class Tester {
static num _runControlTest(List list) {
var stopwatch = Stopwatch();
stopwatch.start();
list.sort();
stopwatch.stop();
return stopwatch.elapsedMicroseconds;
}
static void runTests(List list, String groupName,
{bool useRandomPivot = false, bool sortFromConstructor = false}) {
// CONTROL
var controlElapsedTimeMicroSec = 0;
controlElapsedTimeMicroSec = _runControlTest(list);
// END CONTROL
var expectedRecursionCountLogN = log2(list.length).ceil();
var expectedRecursionCountNLogN = list.length * expectedRecursionCountLogN;
var qs = QuickSort();
var stopwatch = Stopwatch();
stopwatch.start();
list =
qs.sort(iList: list, useRandomPivot: useRandomPivot); // METHOD TO TEST
stopwatch.stop();
var elapsedTimeMicroSec = stopwatch.elapsedMicroseconds;
var recursionCount = qs
.recurseCount; //NOTE (BUG in Dart?): unable to get the recurseCount correctly from within this class/method
group(groupName, () {
var testSubject =
'Time Taken: ${controlElapsedTimeMicroSec} microseconds';
var reason =
'The built in sort took ${controlElapsedTimeMicroSec} microseconds, while the test took ${elapsedTimeMicroSec}.';
test(testSubject, () {
expect(
elapsedTimeMicroSec, lessThanOrEqualTo(controlElapsedTimeMicroSec),
reason: reason);
});
testSubject =
'Time Complexity: ${recursionCount} vs ${expectedRecursionCountNLogN}';
reason =
'Time Complexity of ${recursionCount} is greater than either range (LogN) ${expectedRecursionCountLogN} or (N*LogN) ${expectedRecursionCountNLogN}';
test(testSubject, () {
expect(recursionCount, lessThanOrEqualTo(expectedRecursionCountNLogN),
reason: reason);
});
});
}
}
void main() {
var min = 0;
var len = 1000000;
var max = len;
var originalList = getRandomIntList(min: min, max: max, len: len);
var list = List.from(originalList);
// BUG? When called within this Tester.runTests the sort method does NOT return the correct recurseCount
Tester.runTests(list, 'UNSORTED_RIGHT_PIVOT', useRandomPivot: false);
list = List.from(originalList);
var qs = QuickSort();
// When called directly from main() the sort method DOES return the correct recurseCount
var stopwatch = Stopwatch()..start();
qs.sort(iList: list, useRandomPivot: true); //METHOD TO TEST
stopwatch.stop();
group('UNSORTED_RANDOM_PIVOT', () {
test('Time Taken: ${stopwatch.elapsedMicroseconds} microseconds', () {
expect(stopwatch.elapsedMicroseconds,
lessThanOrEqualTo(Tester._runControlTest(originalList)));
});
var nLogN = (list.length * (log2(list.length).ceil()));
test('Time Complexity: ${qs.recurseCount} vs $nLogN', () {
expect(qs.recurseCount, lessThanOrEqualTo(nLogN));
});
});
}

Need help in simplifying the code to synchronise tooltips and crosshairs for Highcharts,

I have created a series of synchronised charts, the code to link the crosshair and tooltip is rather complicated:
function syncronizeCrossHairs(chart) {
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
var container = $(chart.container),
offset = container.offset(),
x;
container[0].addEventListener(eventType,
(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
//remove old plot line and draw new plot line (crosshair) for this chart
var xAxis1 = chart1.xAxis[0],
points = [],
points1 = [],
points2 = [],
points3 = [],
e = chart1.pointer.normalize(evt); // Find coordinates within the chart
chart1.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points.push(point)
}
})
if (points) {
var number = 0;
Highcharts.each(points, function(p, i) {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
if (points.length) {
chart1.tooltip.refresh(points); // Show the tooltip
}
}
xAxis1.drawCrosshair(x, points[0])
/*----- second chart ------*/
var xAxis2 = chart2.xAxis[0];
chart2.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points1.push(point)
}
})
if (points1[0]) {
var number = 0;
Highcharts.each(points1, function(p, i) {
if (!p.series.visible) {
points1.splice(i - number, 1);
number++;
}
})
if (points1.length) {
chart2.tooltip.refresh(points1); // Show the tooltip
}
}
xAxis2.drawCrosshair(x, points1[0])
/*----- third chart ------*/
var xAxis3 = chart3.xAxis[0];
chart3.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points2.push(point)
}
console.log(points2)
})
if (points2[0]) {
var number = 0;
Highcharts.each(points1, function(p, i) {
if (!p.series.visible) {
points2.splice(i - number, 1);
number++;
}
})
if (points2.length) {
chart3.tooltip.refresh(points2); // Show the tooltip
}
}
xAxis3.drawCrosshair(x, points2[0])
/* ----- fourth chart ------ */
var xAxis4 = chart4.xAxis[0];
chart4.series.forEach(s => {
var point = s.searchPoint(e, true)
if (point) {
point.setState();
points3.push(point)
}
})
if (points3[0]) {
var number = 0;
Highcharts.each(points3, function(p, i) {
if (!p.series.visible) {
points3.splice(i - number, 1);
number++;
}
})
if (points3.length) {
chart4.tooltip.refresh(points3); // Show the tooltip
}
}
xAxis4.drawCrosshair(x, points3[0])
}))
})
}
How ever I have found a better example here: http://jsfiddle.net/mushigh/a3kjrz6u/
$('#container').bind('mousemove touchmove touchstart', function(e) {
var chart,
point,
i,
event;
for (i = 0; i < Highcharts.charts.length; i = i + 1) {
chart = Highcharts.charts[i];
event = chart.pointer.normalize(e.originalEvent); // Find coordinates within the chart
point = chart.series[0].searchPoint(event, true); // Get the hovered point
if (point) {
point.onMouseOver(); // Show the hover marker
chart.tooltip.refresh(point); // Show the tooltip
chart.xAxis[0].drawCrosshair(event, point); // Show the crosshair
}
}
});
/**
* Override the reset function, we don't need to hide the tooltips and crosshairs.
*/
Highcharts.Pointer.prototype.reset = function() {
return undefined;
};
How do I adapt my code here: https://jsfiddle.net/ashenshugar/716jx4n9/
To use the simplified code in my example
I don't think that the demo which you have found is a good approach to your requirements.
The demo needs to the specific data structure, like: https://github.com/highcharts/highcharts/blob/master/samples/data/activity.json
The demo is an example to show tooltip for only one series per chart, meanwhile, you have got a few and a shared tooltip, so finally, you will need to do the same calculations to get the array of points for shared tooltip.
Except that, I think that a better approach is to clean up your code.
Notice that the functionalities to calculate points for each chart are similar and can be paste into the loop:
function syncronizeCrossHairs(chart) {
['mousemove', 'touchmove', 'touchstart'].forEach(function(eventType) {
var container = $(chart.container),
offset = container.offset(),
x;
container[0].addEventListener(eventType,
(function(evt) {
x = evt.clientX - chart.plotLeft - offset.left;
Highcharts.charts.forEach(ch => {
var e = ch.pointer.normalize(evt), // Find coordinates within the chart
points = [];
ch.series.forEach(s => {
var point = s.searchPoint(e, true);
if (point) {
point.setState();
points.push(point)
}
})
if (points) {
var number = 0;
Highcharts.each(points, function(p, i) {
if (!p.series.visible) {
points.splice(i - number, 1);
number++;
}
})
if (points.length) {
ch.tooltip.refresh(points); // Show the tooltip
}
}
ch.xAxis[0].drawCrosshair(x, points[0])
})
}))
})
}
And notice that also your afterSetextreme callback can be change to trigger this function:
function setExtremes(chart, min, max) {
Highcharts.charts.forEach(ch => {
if (ch !== chart) {
ch.xAxis[0].setExtremes(min, max)
}
})
}
Also, you can define options which are common for each chart, like it is done here: https://jsfiddle.net/gh/get/library/pure/highcharts/highcharts/tree/master/samples/highcharts/demo/gauge-solid/
Finally demo: https://jsfiddle.net/BlackLabel/hv7azdgm/

Lock Orientation When Using OpenLayers Geolocation

We use an embedded map to track our location while driving in the field. Currently the map rotates to match the GPS's orientation. We've found that to be very disorienting and I'd like to lock the orientation North (0 degrees). I still would like the map to track location and indicate heading if available. Below is the snipped from the map's javascript file pertaining to geolocation.
map.addLayer(addressLayer);
// Geolocation marker
var markerEl = document.getElementById('geolocation_marker');
var marker = new ol.Overlay({
positioning: 'center-center',
element: markerEl,
stopEvent: false
});
map.addOverlay(marker);
// LineString to store the different geolocation positions. This LineString
// is time aware.
// The Z dimension is actually used to store the rotation (heading).
var positions = new ol.geom.LineString([],
/** #type {ol.geom.GeometryLayout} */ ('XYZM'));
// Geolocation Control
var geolocation = new ol.Geolocation(/** #type {olx.GeolocationOptions} */ ({
projection: view.getProjection(),
tracking: true,
trackingOptions: {
maximumAge: 10000,
enableHighAccuracy: true,
timeout: 600000
}
}));
var deltaMean = 500; // the geolocation sampling period mean in ms
// Listen to position changes
geolocation.on('change', function(evt) {
var position = geolocation.getPosition();
var accuracy = geolocation.getAccuracy();
var heading = geolocation.getHeading() || 0;
var speed = geolocation.getSpeed() || 0;
var m = Date.now();
addPosition(position, heading, m, speed);
map.getView().setCenter(geolocation.getPosition());
document.getElementById("locate").style.backgroundColor = 'rgba(0,128,0,1)';
locateUser = true;
});
geolocation.on('error', function(error) {
var errors = {
1: 'Permission denied to locate device',
2: 'Position unavailable',
3: 'Request timeout'
};
if (error.code){
document.getElementById("locate").style.backgroundColor = 'rgba(255,0,0,1)';
locateUser = false;
}
alert("Error: " + errors[error.code]);
});
// convert radians to degrees
function radToDeg(rad) {
return rad * 360 / (Math.PI * 2);
}
// convert degrees to radians
function degToRad(deg) {
return deg * Math.PI * 2 / 360;
}
// modulo for negative values
function mod(n) {
return ((n % (2 * Math.PI)) + (2 * Math.PI)) % (2 * Math.PI);
}
function addPosition(position, heading, m, speed) {
var x = position[0];
var y = position[1];
var fCoords = positions.getCoordinates();
var previous = fCoords[fCoords.length - 1];
var prevHeading = previous && previous[2];
if (prevHeading) {
var headingDiff = heading - mod(prevHeading);
// force the rotation change to be less than 180°
if (Math.abs(headingDiff) > Math.PI) {
var sign = (headingDiff >= 0) ? 1 : -1;
headingDiff = - sign * (2 * Math.PI - Math.abs(headingDiff));
}
heading = prevHeading + headingDiff;
}
positions.appendCoordinate([x, y, heading, m]);
// only keep the 20 last coordinates
positions.setCoordinates(positions.getCoordinates().slice(-20));
// FIXME use speed instead
if (heading && speed) {
markerEl.src = 'images/geolocation_marker_heading.png';
} else {
markerEl.src = 'images/geolocation_marker.png';
}
}
var previousM = 0;
// change center and rotation before render
map.beforeRender(function(map, frameState) {
if (frameState !== null) {
// use sampling period to get a smooth transition
var m = frameState.time - deltaMean * 1.5;
m = Math.max(m, previousM);
previousM = m;
// interpolate position along positions LineString
var c = positions.getCoordinateAtM(m, true);
var view = frameState.viewState;
if (c) {
view.rotation = -c[2];
marker.setPosition(c);
}
}
return true; // Force animation to continue
});
// postcompose callback
function render() {
map.render();
}
// geolocate device
var geolocateBtn = document.getElementById('locate');
geolocateBtn.addEventListener('click', function() {
if(locateUser){
geolocation.setTracking(false);
geolocateBtn.style.backgroundColor = 'rgba(255,0,0,1)';
locateUser = false;
}
else{
geolocation.setTracking(true);
map.getView().setCenter(geolocation.getPosition());
geolocateBtn.style.backgroundColor = 'rgba(0,128,0,1)';
map.on('postcompose', render);
map.render();
locateUser = true;
}
}, false);
addLocations(QueryString);
function addLocations(addressArr) {
if (nextAddress < addressArr.length) {
setTimeout(function(){
if (addressArr[nextAddress] !== undefined){
geocodeAddress(addressArr[nextAddress]);
}
}, delay);
}
if(nextAddress == addressArr.length) {
view.fitExtent(vectorSource.getExtent(), map.getSize());
}
}
function geocodeAddress (location) {
$.getJSON('http://maps.googleapis.com/maps/api/geocode/json?address='+location.address+'&sensor=false', null, function (data) {
if(data.status === 'OK'){
var p = data.results[0].geometry.location;
var color = location.status == 'incomplete' ? 'red' : 'green';
var pointFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.transform([p.lng, p.lat], 'EPSG:4326',
'EPSG:3857')),
fillColor: color,
id: location.id
});
vectorSource.addFeature(pointFeature);
addresses.push(pointFeature);
nextAddress+=1;
addLocations(QueryString);
}
if(data.status === 'OVER_QUERY_LIMIT'){
delay += delay;
}
});
}
Here is the relevant ol3 code that's causing the rotation to happen.
By setting view.rotation = 0; you may resolve the issue.

Javascript - getElementID from scratch using BFS?

I'm trying to learn javascript, and spent tonight writing a getElementByID() function using Breadth-First Search. In short: I'm lost.
Fiddle: http://jsfiddle.net/timdown/a2Fm6/
Code:
var nodes = [];
function getElementById(node, id) {
alert(nodes.length);
if (node.childNodes[i].id == id) {
return node.childNodes[i];
} else if (node.childNodes[i].length > 0) {
for (var i = 0, len = node.childNodes.length; i < len; ++i) {
nodes.push(node.childNodes[i]);
}
}
if (nodes.length > 0) {
getElementById(nodes[0], id);
}
}
var el = getElementById(document.body, 'id');
Any help?
You're missing a for loop in the top half of your code. Where is i defined?
Here's how I'd write it:
function getElementById(node, id) {
//An array of all the nodes at the same depth
var nodes = [node];
//While the array is not empty
while(nodes.length) {
var newNodes = [];
for(var i = 0; i < nodes.length; i++) {
var children = nodes[i].childNodes;
for(var j = 0; j < children.length; j++) {
var child = children[j];
if(child.id == id) {
return child
}
newNodes.push(child);
}
}
//Replace nodes with an array of the nodes the next level down
nodes = newNodes
}
}

Actionscript 2: Tween running extremely slow

I am using the following code to tween an movieclip once _global.choiceMade equals 1...
onClipEvent (load) {
import mx.transitions.Tween;
import mx.transitions.easing.*;
}
onClipEvent (enterFrame) {
if (_global.choiceMade == 1) {
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
}
}
This is contained within each frame of the main timeline. The first time it runs fine but on the next frame it runs incredibly slow (takes about 12 seconds not 0.5 and is very choppy), if I then return to the first frame and run it again now this time it is extremely slow.
I can't work out why its doing this my CPU stays around 6-15% while its running so it can't be too demanding.
Updated to show rest of my code:
On main timeline have a frame each containing a movieclip. On the timeline of each of these movieclips contains:
//tint an object with a color just like Effect panel
//r, g, b between 0 and 255; amount between 0 and 100
Color.prototype.setTint = function(r, g, b, amount) {
var percent = 100-amount;
var trans = new Object();
trans.ra = trans.ga=trans.ba=percent;
var ratio = amount/100;
trans.rb = r*ratio;
trans.gb = g*ratio;
trans.bb = b*ratio;
this.setTransform(trans);
};//Robert Penner June 2001 - http://www.robertpenner.com
MovieClip.prototype.scaleXY = function(to){
this.onEnterFrame = function(){
this._alpha = to-(to-this._alpha)/1.2;
if(this._alpha > to-1 && this._alpha < to+1){
this._alpha = to;
delete this.onEnterFrame
}
}
}
scoreUpdated = 0;
Answer = 1;
_global.choiceMade = 0;
Buttons = new Array(this.buttonHolder.True, this.buttonHolder.False);
Answers = new Array(this.Correct, this.Wrong);
for (i=0; i<Answers.length; i++) {
Answers[i]._alpha = 0;
}
for (b=0; b<Buttons.length; b++) {
Buttons[b].thisValue = b;
}
In this movieclip there are two movieclip buttons (True and False) containing this code:
onClipEvent (enterFrame) {
this.onRollOver = function() {
this.gotoAndStop("over");
};
this.onRollOut = function() {
this.gotoAndStop("up");
};
this.onPress = function() {
this.gotoAndStop("down");
};
this.onReleaseOutside = function() {
this.gotoAndStop("up");
};
this.onRelease = function() {
this.gotoAndStop("down");
whichChoice = this;
_global.choiceMade = 1;
counter = 0;
};
if (_global.choiceMade == 1) {
this.enabled = false;
this._parent.scoreNow = _global.score;
this._parent.scoreOutOf = (this._parent._parent._currentframe)- 1 + ( _global.choiceMade);
if (thisValue == this._parent._parent.Answer && whichChoice == this) {
myColor = new Color(this);
myColor.setTint(0,204,0,13);
this._parent._parent.Answers[0]._alpha = 100;
this._parent._parent.Answers[0].scaleXY(100);
this.tick.swapDepths(1000);
if (counter == 0) {
_global.score++;
counter++;
}
}
else if (thisValue == this._parent._parent.Answer) {
myColor = new Color(this);
myColor.setTint(0,204,0,13);
this.tick.swapDepths(1000);
}
else if (whichChoice == this) {
this._parent._parent.Answers[1]._alpha = 100;
this._parent._parent.Answers[1].scaleXY(100);
myColor = new Color(this);
myColor.setTint(255,0,0,13);
this.cross.swapDepths(1000);
}
else {
myColor = new Color(this);
myColor.setTint(255,0,0,13);
myColor.setTint(255,0,0,13);
this.cross.swapDepths(1000);
}
}
}
The script at the top is on a movieclip these buttons are contained in called buttonHolder which does what it says, and tweens the buttons across the screen to reveal a next button once an answer is chosen.
From what I can see, as long as you have choiceMade == 1 you create a new effect! which is not ok. because in 1 sec at 15 fps you will have 15 tweens running :(
try yo set choiceMade = 0 or somehting else than 1
onClipEvent (enterFrame)
{
if (_global.choiceMade == 1)
{
_global.choiceMade = -1;
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
}
}
Without seeing the rest of your code it's hard to see exactly what's going on. But it looks like you never change choiceMade and it continuously recreates the tween.
onClipEvent (enterFrame) {
if (_global.choiceMade == 1) {
var myTweenX:Tween = new Tween(this, "_x", mx.transitions.easing.Back.easeOut, this._x, -349, 0.5, True);
_global.choiceMade = 0;
}
}

Resources