Related
To show all bars I have set the horizontal scrolling to chartjs in Ionic angular project , and i have used the DataLabelsPlugin constant for bars label. and while scrolling the datalabels is overlapping with y-axis its not hiding before y-axis like bars.
and also horizontal scroll is not happening smoothly.
graph working fine as a expected output
marked with issue about - after scrolling the datalabels went over the y-axis not hide below y-axis like bars
I have tried to add and used the custom datalabels but same issue i am getting and i didnt find any css or attribute on 'https://www.chartjs.org/docs/latest/' official site or not on any other sites -> to hide the datalabels from over the y-axis.
ts file code:
createBarChart() {
const footer = (tooltipItems) => {
let sum = 0;
tooltipItems.forEach(function(tooltipItem) {
sum += tooltipItem.parsed.y;
});
return this.util.getFormatValue(sum)+'%';
};
const toolLabel = (tooltipItems) => {
return "";
};
const toolTitle = (tooltipItems) => {
var string_to_array = function (str) {
return str.trim().split("#$#$");
};
var ss;
tooltipItems.forEach(function(tooltipItem) {
ss = string_to_array(tooltipItem.label.replace(/(.{40})/g, "$1#$#$"))
});
return ss;
};
let graphSize = Math.max(...this.daywise_occupancy);
if(graphSize == 0){
graphSize =1;
}
const plugin = {
id: 'customCanvasBackgroundColor',
beforeDraw: (chart, args, options) => {
const {ctx} = chart;
ctx.save();
ctx.globalCompositeOperation = 'destination-over';
ctx.fillStyle = '#ffffff';
ctx.fillRect(0, 0, chart.width, chart.height);
ctx.restore();
}
};
this.bars = new Chart(this.barchart6.nativeElement, {
type: 'bar',
data: {
labels: this.daywise_date,
datasets: [{
data: this.daywise_occupancy,
backgroundColor: function(context) {
var value = context.dataset.data[context.dataIndex];
return value <= 15 ? '#f95959'
: value > 15 && value <=60 ? '#F5A623'
: '#00ADB5'
},
borderColor: function(context) {
var value = context.dataset.data[context.dataIndex];
return value <= 15 ? '#f95959'
: value > 15 && value <=60 ? '#F5A623'
: '#00ADB5'
},
borderWidth: 1,
barThickness:30,
}]
},
plugins: [DataLabelsPlugin,plugin],
options: {
animations: {
tension: {
duration: 1000,
easing: 'linear',
from: 1,
to: 0,
loop: true
}
},
scales: {
x: {
min:0,
max:5,
ticks : {
maxRotation: 70,
minRotation: 70,
font:{
size:10,
},
callback: function(value : any, index, ticks_array) {
let characterLimit = 12;
let label = this.getLabelForValue(value);
if ( label.length >= characterLimit) {
return label.slice(0, label.length).substring(0, characterLimit -1).trim() + '..';
}
return label;
}
}
},
y: { // defining min and max so hiding the dataset does not change scale range
min: 0,
max: this.loader.getGraphsizeRound(graphSize),
title: { display: true, text: (this.titleSet)? '% of Branches Contribution' : '% of Seat Occupancy' },
beginAtZero: true,
display: true,
position: 'left',
// ticks: {
// stepSize: 6,
// },
}
},
plugins: {
legend: {
display: false
},
datalabels:{
anchor: 'end',
align: 'end',labels: {
value: {
color: '#2C3A45;',
formatter: function (value) {
// return Math.round(value) + '%';
return value + '%';
},
font:{
weight:700,
size:14
}
}
}
},
tooltip: {
callbacks: {
footer:footer,
label: toolLabel,
title:toolTitle
},
displayColors:false
}
}
}
});
this.bars.canvas.addEventListener('touchmove',(eve) => {
this.touchmove(eve,this.bars)
});
this.bars.canvas.addEventListener('touchstart',(eve) => {
this.touchstart(eve)
});
}
touchstart(e)
{
this.startX = e.touches[0].clientX;
this.startY = e.touches[0].clientY;
}
touchmove(e,chart)
{
var deltaX = e.touches[0].clientX - this.startX,
deltaY = e.touches[0].clientY - this.startY;
const dataLength = chart.data.labels.length;
let min = chart.options.scales.x.min;
if(deltaX < 0){
if( chart.options.scales.x.max >= dataLength ){
chart.options.scales.x.min = dataLength - 5;
chart.options.scales.x.max = dataLength;
}else{
chart.options.scales.x.min += 1;
chart.options.scales.x.max += 1;
}
// console.log( chart.options.scales.x.min);
// chart1line.options.scales.y.max = graphSize
}else if(deltaX > 0){
if( chart.options.scales.x.min <= 0 ){
chart.options.scales.x.min = 0;
chart.options.scales.x.max = 4;
}else{
chart.options.scales.x.min -= 1;
chart.options.scales.x.max -= 1;
}
}else{
}
chart.update();
}
HTML code:
<div class="chartWrapper">
<div class="chartAreaWrapper">
<canvas #barchart6 height="190" max-height="190" width="0"></canvas>
</div>
</div>
My expected output
horizontal scroll work smoothly.
after scrolling label should not overlap on y-axis.
I want to use this game https://github.com/yum650350/tissuebox in my Flutter project. I tried calling different parts of the main page but it wasn't coming up properly even though the game itself is working.
I want to integrate it in my iOS app and want to call it with a
function which opens a separate page where the user can play the game.
Is there a way I can do it?
So this is what I tried:
I called the method of the game in a separate screen
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(new MaterialApp(
debugShowCheckedModeBanner: false,
home: FirstScreen(),
));
}
class FirstScreen extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Screen'),
),
body: Center(
child: RaisedButton(
color: Colors.red,
child: Text('Go to Second Screen'),
onPressed: () {
Navigator.of(context)
.push(MaterialPageRoute<Null>(builder: (BuildContext context) {
**???**
}));
},
),
),
);
}
}
But I don't know what to call in ?? this part since the game isn't wrapped in any class. This is the code of the game I want to call.
import 'package:shared_preferences/shared_preferences.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/gestures.dart';
import 'package:flame/sprite.dart';
import 'package:flame/flame.dart';
import 'package:flame/util.dart';
import 'package:flame/game.dart';
import 'dart:math';
tissuemain() async {
var util = Util();
await util.fullScreen();
await util.setOrientation(DeviceOrientation.portraitUp);
//loadimages
//tissuebox : 0,1,2,3,4,5,6
//background : b
//crown : c
//tissue : t
await Flame.images.loadAll(['b', '0', '1', '2', '3', '4', '5', '6', 't', 'c']);
audioLoad(c) async => (await Flame.audio.load(c)).path;
setAudio(a, s, v) async {
await a.setUrl(await audioLoad(s), isLocal: true);
a.setVolume(v);
}
//audios
//single drag : s.mp3
//double drag : s.mp3
//triple drag : s.mp3
//tick tock : tk.mp3
//game over : a.mp3
GameTable.setAudioList(GameTable.audioList1, await audioLoad('s.mp3'));
GameTable.setAudioList(GameTable.audioList2, await audioLoad('d.mp3'));
GameTable.setAudioList(GameTable.audioList3, await audioLoad('t.mp3'));
await setAudio(GameTable.tickTock, 'tk.mp3', 1.0);
await setAudio(GameTable.gameOver, 'a.mp3', .5);
var game = GameTable((await SharedPreferences.getInstance()).getInt('hs') ?? 0);
var hDrag = HorizontalDragGestureRecognizer();
var vDrag = VerticalDragGestureRecognizer();
hDrag.onUpdate = game.onDragUpdate;
hDrag.onStart = game.onDragStart;
hDrag.onEnd = game.onDragEnd;
vDrag.onUpdate = game.onDragUpdate;
vDrag.onStart = game.onDragStart;
vDrag.onEnd = game.onDragEnd;
runApp(game.widget);
util.addGestureRecognizer(hDrag);
util.addGestureRecognizer(vDrag);
}
enum Drag { tissue, box, none }
class GameTable extends Game {
//Audio
//These are workarounds for the ios memory leak
static var
tickTock = AudioPlayer(),
gameOver = AudioPlayer(),
audioList1 = [AudioPlayer(), AudioPlayer(), AudioPlayer()],
audioList2 = [AudioPlayer(), AudioPlayer()],
audioList3 = [AudioPlayer(), AudioPlayer()],
audioIndex1 = 0,
audioIndex2 = 0,
audioIndex3 = 0;
static getPlayIndex(int audioPlayer) {
if (audioPlayer == 1)
audioIndex1 = audioIndex1 < audioList1.length - 1 ? audioIndex1 + 1 : 0;
else if (audioPlayer == 2)
audioIndex2 = audioIndex2 < audioList2.length - 1 ? audioIndex2 + 1 : 0;
else if (audioPlayer == 3) audioIndex3 = audioIndex3 < audioList3.length - 1 ? audioIndex3 + 1 : 0;
return audioPlayer == 1 ? audioIndex1 : audioPlayer == 2 ? audioIndex2 : audioIndex3;
}
static get tissue1 => audioList1[getPlayIndex(1)];
static get tissue2 => audioList2[getPlayIndex(2)];
static get tissue3 => audioList3[getPlayIndex(3)];
static setAudioList(List<AudioPlayer> al,String audioName) => al.forEach((x) {
x.setUrl(audioName, isLocal: true);
x.setVolume(.2);
});
//
var background = Sprite('b'),
crown = Sprite('c'),
initialPoint = Offset.zero,
destPoint = Offset.zero,
dragState = Drag.none,
gameing = false,
gameover = false,
timePass = .0,
heighScore = 0,
score = 0,
timePassTemp = 0;
double tileSize, point1;
double get k => screenSize.width / 5 / tileSize;
Size screenSize;
Rect rect;
TissueBox tissueBox;
saveHighScore() async => await (await SharedPreferences.getInstance()).setInt('hs', heighScore);
GameTable(this.heighScore) {
init();
}
init() async {
resize(await Flame.util.initialDimensions());
rect = Rect.fromLTWH(.0, screenSize.height - tileSize * 23, tileSize * 9, tileSize * 23);
tissueBox = TissueBox(this);
}
#override
render(Canvas c) {
paintText(txt, offset, center, fontSize) {
var painter = TextPainter(
text: TextSpan(
style: TextStyle(
color: Colors.white,
fontSize: fontSize,
fontFamily: 'NS'),
text: txt),
textScaleFactor: k,
textDirection: TextDirection.ltr);
painter.layout();
painter.paint(c, center ? Offset(offset.dx - painter.width / 2, offset.dy) : offset);
}
background.renderRect(c, rect);
tissueBox.render(c);
var horCenter = tissueBox.initialLeft + tissueBox.boxRect.width / 2;
if (gameing)
paintText(timePass.toStringAsFixed(timePass < 1 ? 1 : 0) + 's', Offset(horCenter + 8, k * 23), true, k * 10);
var heighScoreTxt = heighScore.toString();
paintText(heighScoreTxt, Offset(heighScoreTxt.length==1?44.0:heighScoreTxt.length>2?22.0:33.0, k * 30), false, k * 12);
crown.renderRect(c, Rect.fromLTWH(28.0, k * 10, 49.2, 39.0));
paintText(score.toString(), Offset(horCenter, k * 50), true, k * 25);
heighScore = score > heighScore ? score : heighScore;
}
#override
update(double t) {
tissueBox.update(t);
timePass -= gameing || gameover ? t : 0;
if (timePass < 0 && gameing) {
tissueBox.isAway = true;
gameing = false;
timePass = 2;
gameover = true;
saveHighScore();
tissueBox.newGame();
} else if (gameing && !gameover) {
var floor = timePass.floor();
if (floor < timePassTemp && floor < 6 && floor != 0)
TissueBox.delay(Duration(milliseconds: 300), () => GameTable.tickTock.resume());
timePassTemp = floor;
}
gameover = timePass <= 0 && gameover ? false : gameover;
}
resize(s) {
screenSize = s;
tileSize = screenSize.width / 9;
}
onDragStart(DragStartDetails detail) {
var point = detail.globalPosition;
dragState = tissueBox.tissue.rect.contains(point) ? Drag.tissue : tissueBox.boxRect.contains(point) ? Drag.box : Drag.none;
initialPoint = Offset(point.dx == 0 ? initialPoint.dx : point.dx, point.dy == 0 ? initialPoint.dy : point.dy);
point1 = (tissueBox.tissue.rect.left - point.dx).abs();
}
onDragUpdate(DragUpdateDetails detail) {
if (gameover || dragState == Drag.none) return;
var point = detail.globalPosition;
destPoint = Offset(point.dx == 0 ? destPoint.dx : point.dx, point.dy == 0 ? destPoint.dy : point.dy);
if (dragState == Drag.tissue) {
if (initialPoint.dy - destPoint.dy > 100) {
if (gameing != true && gameover != true) {
gameing = true;
timePass = 10;
score = 0;
}
var sub = (point1 - (tissueBox.tissue.rect.left - point.dx).abs()).abs();
var addPoint = sub < 3 ? 3 : sub < 6 ? 2 : 1;
dragState = Drag.none;
tissueBox.nextTissue(addPoint);
playTissueAudio(addPoint);
score += addPoint;
}
} else if (dragState == Drag.box) {
tissueBox.boxRect = Rect.fromLTWH(tissueBox.initialLeft + destPoint.dx - initialPoint.dx, tissueBox.boxRect.top, TissueBox.boxSize.dx, TissueBox.boxSize.dy);
tissueBox.ismoving = true;
}
}
playTissueAudio(i) => (i == 1 ? GameTable.tissue1 : i == 2 ? GameTable.tissue2 : GameTable.tissue3).resume();
onDragEnd(DragEndDetails detail) {
initialPoint = Offset.zero;
dragState = Drag.none;
tissueBox.tissue.isMoving = false;
tissueBox.ismoving = false;
destPoint = initialPoint;
}
}
class TissueBox {
Rect get initialRect => Rect.fromLTWH(boxRect.center.dx - Tissue.width / 2, boxRect.top - boxRect.height + 20.3, Tissue.width, Tissue.width);
Sprite get getBoxSprite =>Sprite( rnd.nextInt(7).toString());
var tissueAwayList = List<TissueAway>(), rnd = Random(), ismoving = false, isAway = false;
Offset get getTissueUpPosition => Offset(initialRect.left, initialRect.top - 150);
final GameTable game;
Sprite boxSprite;
Rect boxRect;
int tissueCount;
Tissue tissue;
double get initialLeft => game.screenSize.width / 2 - TissueBox.boxSize.dx / 2;
double get initialTop => game.screenSize.height - game.tileSize * 5.5;
static var boxSize = Offset(150.0, 100.0);
TissueBox(this.game) {
boxRect = Rect.fromLTWH(initialLeft, initialTop, boxSize.dx, boxSize.dy);
tissueCount = 10 - rnd.nextInt(5);
tissue = Tissue(game, this);
boxSprite = getBoxSprite;
}
render(Canvas c) {
boxSprite.renderRect(c, boxRect);
tissue.render(c);
tissueAwayList.forEach((x) => x.render(c));
}
update(double t) {
tissue.update(t);
tissueAwayList.removeWhere((x) => x.isAway);
tissueAwayList.forEach((x) => x.update(t));
var distense = boxRect.left - initialLeft;
if (ismoving && !game.gameover) {
if (distense.abs() > 50 && tissueCount == 0){
isAway = true;
}
} else if (isAway && !game.gameover) {
boxRect = boxRect.shift(Offset(distense > 0 ? boxRect.left + game.k * 11 : boxRect.left - game.k * 11, boxRect.top));
if (boxRect.right < -50 || boxRect.left > game.screenSize.width + 50) {
newBox();
}
} else if (isAway && game.gameover) {
var target = Offset(boxRect.left, game.screenSize.height + Tissue.width) - Offset(boxRect.left, boxRect.top);
boxRect = boxRect.shift(
game.k * 11 < target.distance ?
Offset.fromDirection(target.direction, game.k * 11)
: target);
} else {
var target = Offset(initialLeft, initialTop) - Offset(boxRect.left, boxRect.top);
boxRect = boxRect.shift(
game.k * 11 < target.distance ?
Offset.fromDirection(target.direction, game.k * 11)
: target);
}
}
nextTissue(int pointsAdd) {
var duration = Duration(milliseconds: 100);
tissueAwayList.add(TissueAway(game, this));
if (pointsAdd > 1)
delay(duration, () {
tissueAwayList.add(TissueAway(game, this));
if (pointsAdd > 2)
delay(duration, () {
tissueAwayList.add(TissueAway(game, this));
});
});
tissue = Tissue(game, this, --tissueCount == 0);
}
newBox() {
boxSprite = getBoxSprite;
boxRect = Rect.fromLTWH(boxRect.right < -0 ? game.screenSize.width + 50 - boxSize.dx : -50.0, initialTop, boxSize.dx, boxSize.dy);
tissueCount = 10 - rnd.nextInt(5);
tissue = Tissue(game, this);
isAway = false;
ismoving = false;
}
newGame() async {
isAway = true;
GameTable.gameOver.resume();
await delay(Duration(seconds: 2), () {});
newBox();
}
static delay(duration, func()) async => await Future.delayed(duration, func);
}
class Tissue {
var tissueSprite = Sprite('t'), isMoving = false;
static var width = 100.0;
final TissueBox tissueBox;
final GameTable game;
bool isAway;
Rect rect;
Tissue(this.game, this.tissueBox, [this.isAway = false]) {
rect = tissueBox.initialRect;
}
render(Canvas c) => tissueSprite.renderRect(c, rect);
update(double t) => rect = isAway ? rect.shift(Offset.infinite) : tissueBox.initialRect;
}
class TissueAway extends Tissue {
TissueAway(GameTable game, TissueBox tissueBox) : super(game, tissueBox);
render(Canvas c) => tissueSprite.renderRect(c, rect);
update(double t) {
var speed = 500 * t;
Offset target = tissueBox.getTissueUpPosition - Offset(rect.left, rect.top);
if (speed < target.distance)
rect = rect.shift(Offset.fromDirection(target.direction, speed));
else
isAway = true;
}
}
The code in the main method will have a line that says runApp(...). The value of the ... is a widget that will be treated as the root widget of the app. In theory, you could just take that widget and pass it to your Navigator.push method and it should treat that widget as any other widget.
That being said, real life probably won't be as clean as this. There might be some initialization code in that app's main method or root widget that won't work properly if the app has long since already been initialized. Since virtually every Flutter app's root widget creates a WidgetsApp (or one of its derived classes MaterialApp or CupertinoApp), there might be some conflict that arrives from having one of those widgets being inserted as a descendent of another one of those widgets.
The game in question may work simply, or it might take some tweaking to work properly. That's something that will depend entirely on what app you are trying to embed into your own, so the only solution is to try it out and see for yourself.
using the stickman example of http://fabricjs.com/,
I have been trying to achieve moving the related circles when a line is moved. The code in the example is not well structured, heavy & with errors :), as I can not to move the the related circles symmetrically.
If in the //move the other circle part is used next line
obj.set({
'left': (s.calcLinePoints().x1 + _l),
'top': (-s.calcLinePoints().y1 + _t)
});
the difference is in the sign of collected information for y1 and we move some horizontal line visually the result OK, but in my opinion this type of "adjustment" is not the correct one...
[example code]
$(function() {
//create the fabriccanvas object & disable the canvas selection
var canvas = new fabric.Canvas('c', {
selection: false
});
//move the objects origin of transformation to the center
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
function makeCircle(left, top, line1, line2, usedLine, usedEnd) {
//used line - used line for the center
//usedEnd - fromt the used line
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 2,
radius: 6,
fill: '#fff',
stroke: '#666'
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
//add information which line end is used for center
var _usedLineName;
if (usedLine == 1) {
_usedLineName = line1.name;
} else {
_usedLineName = line2.name;
}
c.usedLineName = _usedLineName;
c.usedEndPoint = usedEnd;
return c;
}
function makeLine(coords, name) {
var l = new fabric.Line(coords, {
stroke: 'red',
strokeWidth: 4,
selectable: true, //false
name: name
});
l.hasControls = l.hasBorders = false;
return l;
}
//initial shape information
var line = makeLine([250, 125, 350, 125], "l1"),
line2 = makeLine([350, 125, 350, 225], "l2"),
line3 = makeLine([350, 225, 250, 225], "l3"),
line4 = makeLine([250, 225, 250, 125], "l4");
canvas.add(line, line2, line3, line4);
canvas.add(
makeCircle(line.get('x1'), line.get('y1'), line4, line, 1, 2),
makeCircle(line.get('x2'), line.get('y2'), line, line2, 1, 2),
makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, 1, 2),
makeCircle(line3.get('x2'), line3.get('y2'), line3, line4, 1, 2));
canvas.on('object:moving', function(e) {
//find the moving object type
var objType = e.target.get('type');
var p = e.target;
if (objType == 'circle') {
p.line1 && p.line1.set({
'x2': p.left,
'y2': p.top
});
p.line2 && p.line2.set({
'x1': p.left,
'y1': p.top
});
//set coordinates for the lines - should be done if element is moved programmely
p.line2.setCoords();
p.line1.setCoords();
canvas.renderAll();
} else if (objType == 'line') {
//loop all circles and if some is with coordinates as some of the ends - to change them
for (var i = 0; i < canvas.getObjects('circle').length; i++) {
var currentObj = canvas.getObjects('circle')[i];
if (currentObj.get("usedLineName") == e.target.get('name')) {
//usedEndPoint=2
for (var ss = 0; ss < canvas.getObjects('line').length; ss++) {
var s = canvas.getObjects('line')[ss];
//console.log(s.calcLinePoints())
//console.log(s.calcLinePoints().y2)
var _l = s.left;
var _t = s.top;
if (s.get("name") == currentObj.get("usedLineName")) {
currentObj.set({
'left': (s.calcLinePoints().x2 + _l),
'top': (s.calcLinePoints().y2 + _t)
});
console.log(s.calcLinePoints().y2 + _t)
currentObj.setCoords();
currentObj.line1 && currentObj.line1.set({
'x2': currentObj.left,
'y2': currentObj.top
});
currentObj.line2 && currentObj.line2.set({
'x1': currentObj.left,
'y1': currentObj.top
});
currentObj.line2.setCoords();
currentObj.line1.setCoords();
//move the other circle
canvas.forEachObject(function(obj) {
var _objType = obj.get('type');
if (_objType == "circle" && obj.line2.name == s.get("name")) {
obj.set({
'left': (s.calcLinePoints().x1 + _l),
'top': (s.calcLinePoints().y1 + _t)
});
console.log(s.calcLinePoints().y1 + _t)
obj.setCoords();
obj.line1 && obj.line1.set({
'x2': obj.left,
'y2': obj.top
});
obj.line2 && obj.line2.set({
'x1': obj.left,
'y1': obj.top
});
obj.line2.setCoords();
obj.line1.setCoords();
//canvas.renderAll();
}
});
canvas.renderAll();
//end move oter
}
}
}
}
}
});
});
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="c" width="500" height="500"></canvas>
Here it is the code on jsfiddle, too:
https://jsfiddle.net/muybien/mzsa3z9L/
I want previously to thank you, even only for reading the question.
Thanks of MiltoxBeyond's suggestion, the problem is fixed.
Here it is a working and little cleaned example:
//to save the old cursor position: used on line mooving
var _curX, _curY;
$(function() {
//create the fabriccanvas object & disable the canvas selection
var canvas = new fabric.Canvas('c', {
selection: false
});
//move the objects origin of transformation to the center
fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';
function makeCircle(left, top, line1, line2) {
var c = new fabric.Circle({
left: left,
top: top,
strokeWidth: 2,
radius: 6,
fill: '#fff',
stroke: '#666'
});
c.hasControls = c.hasBorders = false;
c.line1 = line1;
c.line2 = line2;
return c;
}
function makeLine(coords, name) {
var l = new fabric.Line(coords, {
stroke: 'red',
strokeWidth: 4,
selectable: true, //false
name: name
});
l.hasControls = l.hasBorders = false;
return l;
}
//initial shape information
var line = makeLine([250, 125, 350, 125], "l1"),
line2 = makeLine([350, 125, 350, 225], "l2"),
line3 = makeLine([350, 225, 250, 225], "l3"),
line4 = makeLine([250, 225, 250, 125], "l4");
canvas.add(line, line2, line3, line4);
canvas.add(
makeCircle(line.get('x1'), line.get('y1'), line4, line), makeCircle(line.get('x2'), line.get('y2'), line, line2), makeCircle(line2.get('x2'), line2.get('y2'), line2, line3), makeCircle(line3.get('x2'), line3.get('y2'), line3, line4)
);
canvas.on('object:selected', function(e) {
//find the selected object type
var objType = e.target.get('type');
if (objType == 'line') {
_curX = e.e.clientX;
_curY = e.e.clientY;
//console.log(_curX);
//console.log(_curY);
}
});
canvas.on('object:moving', function(e) {
//find the moving object type
var p = e.target;
var objType = p.get('type');
if (objType == 'circle') {
p.line1 && p.line1.set({
'x2': p.left,
'y2': p.top
});
p.line2 && p.line2.set({
'x1': p.left,
'y1': p.top
});
//set coordinates for the lines - should be done if element is moved programmely
p.line2.setCoords();
p.line1.setCoords();
canvas.renderAll();
} else if (objType == 'line') {
var _curXm = (_curX - e.e.clientX);
var _curYm = (_curY - e.e.clientY);
//console.log("moved: " + _curXm);
//console.log("moved: " + _curYm);
//loop all circles and if some contains the line - move it
for (var i = 0; i < canvas.getObjects('circle').length; i++) {
var currentObj = canvas.getObjects('circle')[i];
if (currentObj.line1.get("name") == p.get('name') || currentObj.line2.get("name") == p.get('name')) {
currentObj.set({
'left': (currentObj.left - _curXm),
'top': (currentObj.top - _curYm)
});
currentObj.setCoords();
currentObj.line1 && currentObj.line1.set({
'x2': currentObj.left,
'y2': currentObj.top
});
currentObj.line2 && currentObj.line2.set({
'x1': currentObj.left,
'y1': currentObj.top
});
currentObj.line2.setCoords();
currentObj.line1.setCoords();
}
}
_curX = e.e.clientX;
_curY = e.e.clientY;
}
});
});
canvas {
border: 1px solid #808080;
}
<script src="http://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<canvas id="c" width="500" height="500"></canvas>
Please consider the following code http://jsfiddle.net/franckl/311bcbc8/
var southWest = L.latLng(-90, -180),
northEast = L.latLng(90, 180);
var bounds = L.latLngBounds(southWest, northEast);
var map = L.map('map', {
minZoom: 2,
zoomControl: false,
attributionControl: false,
maxBounds: bounds
});
// Using cartoDB basemap
L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
minZoom: 2,
subdomains: 'abcd',
detectRetina: true,
attribution: ''
}).addTo(map);
map.fitBounds(bounds);
var newMarker0 = L.marker(map.getCenter(), {
icon: new L.Icon.Default(),
zIndexOffset: 10000,
draggable: true
});
newMarker0.addTo(map);
html
<div id="mycontainer">
<div id="map"></div>
</div>
css
body {
margin:0;
padding:0;
}
#map {
position:absolute;
top:0;
bottom:0;
width:300px;
}
#mycontainer {
top: 10px;
width: 600px;
height: 250px;
position: relative;
}
If you drag the marker to the right, it leaves visible area of the map.
How can I prevent the user from dragging the marker outside the map ?
Thanks !
answering my own question in case it helps anyone.
We detect the map container size and check if the marker is going outside the visible area by converting its lat/lng coordinates to a container point (map.containerPointToLatLng(markerContainerPosition))
As a bonus, this code leaves the marker in the same position relative to the map container when the user moves the map. It ensures that the marker never goes outside the visible area (even when zooming)
var southWest = L.latLng(-90, -180),
northEast = L.latLng(90, 180);
var bounds = L.latLngBounds(southWest, northEast);
var map = L.map('map', {
minZoom: 2,
zoomControl: false,
attributionControl: false,
maxBounds: bounds
});
// Using cartoDB basemap
L.tileLayer('https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_all/{z}/{x}/{y}.png', {
minZoom: 2,
subdomains: 'abcd',
detectRetina: true,
attribution: ''
}).addTo(map);
map.fitBounds(bounds);
var newMarker0 = L.marker(map.getCenter(), {
icon: new L.Icon.Default(),
zIndexOffset: 10000,
draggable: true
});
newMarker0.addTo(map);
var mapSize = map.getSize();
var markerContainerPosition = map.latLngToContainerPoint(newMarker0.getLatLng());
function mapMove() {
newMarker0.setLatLng(map.containerPointToLatLng(markerContainerPosition));
}
function markerDrag(e) {
var mTempContainerPos = map.latLngToContainerPoint(newMarker0.getLatLng());
var newPos;
if (mTempContainerPos.x < 20) {
if (mTempContainerPos.y < 45) {
newPos = L.point(20, 45);
} else if (mTempContainerPos.y > (mapSize.y - 20)) {
newPos = L.point(20, mapSize.y - 20);
} else {
newPos = L.point(20, mTempContainerPos.y);
}
} else if (mTempContainerPos.x > mapSize.x - 20) {
if (mTempContainerPos.y < 45) {
newPos = L.point(mapSize.x - 20, 45);
} else if (mTempContainerPos.y > (mapSize.y - 20)) {
newPos = L.point(mapSize.x - 20, mapSize.y - 20);
} else {
newPos = L.point(mapSize.x - 20, mTempContainerPos.y);
}
} else {
if (mTempContainerPos.y < 45) {
newPos = L.point(mTempContainerPos.x, 45);
} else if (mTempContainerPos.y > (mapSize.y - 20)) {
newPos = L.point(mTempContainerPos.x, mapSize.y - 20);
}
}
if (newPos) {
markerContainerPosition = newPos;
newMarker0.setLatLng(map.containerPointToLatLng(newPos));
} else {
markerContainerPosition = mTempContainerPos;
}
}
map.on('move', mapMove);
newMarker0.on('drag', markerDrag);
A solution with slightly more generic code and tailored to dragging the marker rather than the map, but derivative of #Franckl's:
onMarkerDrag: function (event) {
// keep dragged marker within map bounds
var containerPoint = this.map.latLngToContainerPoint(event.target.getLatLng()),
clampX = null,
clampY = null,
MARKER_MARGIN = 10;
if (containerPoint.x - MARKER_MARGIN < 0) {
clampX = MARKER_MARGIN;
} else if (containerPoint.x + MARKER_MARGIN > this.mapContainerBounds.width) {
clampX = this.mapContainerBounds.width - MARKER_MARGIN;
}
if (containerPoint.y - MARKER_MARGIN < 0) {
clampY = MARKER_MARGIN;
} else if (containerPoint.y + MARKER_MARGIN > this.mapContainerBounds.height) {
clampY = this.mapContainerBounds.height - MARKER_MARGIN;
}
if (clampX !== null || clampY !== null) {
if (clampX !== null) { containerPoint.x = clampX; }
if (clampY !== null) { containerPoint.y = clampY; }
marker.setLatLng(this.map.containerPointToLatLng(containerPoint));
}
},
I derive this.mapContainerBounds once on map init instead of every time the drag handler fires (my map does not change size), like this:
this.mapContainerBounds = mapDOMNode.getBoundingClientRect();
Here is the Script i'm using to stream my tumblr blog posts to my website, i have the correct API Key, and Base host name, still nothing appears. Is there some other information I'm bypassing ? any help will be greatly appreciated.
<script type="text/javascript">
var $_api_key = 'PhLtoFx1aCBtu9IG3pIuT9BPd0Vxb602nPZtdMKxHvHM89VH28',
$_base_hostname = 'http://amongtheprimeblog.tumblr.com',
$_full_width = 1170;
$_rows = 2
$_cols = 3;
$_min_width = $_full_width / $_cols,
$_nsfw_tag = 'nsfw';
$_display_limit = $_rows * $_cols,
$_post_limit = $_display_limit * 2,
$_photos_api = 'https://api.tumblr.com/v2/blog/'+$_base_hostname+'/posts/photo?limit='+$_post_limit+'&api_key='+$_api_key,
jQuery.ajax($_photos_api,
{
dataType: 'jsonp',
success: function ($_posts) {
//console.log($_posts);
var $_photos = [],
$_displayed = 0 ;
for(var i=0; i < $_post_limit; i++) {
var $_post_url = $_posts.response.posts[i].post_url.replace('http://','https://'),
$_tag_count = $_posts.response.posts[i].tags.length,
$_photo_count = $_posts.response.posts[i].photos[0].alt_sizes.length,
$_post_photo_big = $_posts.response.posts[i].photos[0].original_size.url.replace('http://','https://'),
$_post_photo_big_width = $_posts.response.posts[i].photos[0].original_size.width,
$_post_photo_big_height = $_posts.response.posts[i].photos[0].original_size.height,
$_can_display = true;
// Check NSFW to keep Adroll happy!
for (var k=0; k < $_tag_count; k++ ){
if($_posts.response.posts[i].tags[k] == $_nsfw_tag) {
$_can_display = false;
}
}
// Iterate for most suitable image size.
if($_can_display) {
for (var j=0; j < $_photo_count; j++ ) {
if($_posts.response.posts[i].photos[0].alt_sizes[j].width > $_min_width) {
$_post_photo = $_posts.response.posts[i].photos[0].alt_sizes[j].url.replace('http://','https://');
} else {
break;
}
}
$_photos.push('<a id="tumblr_'+i+'" class="tumblr_post" href="'+$_post_url+'" target="_blank" data-src-big="'+$_post_photo_big+'" data-src-big-width="'+$_post_photo_big_width+'" data-src-big-height="'+$_post_photo_big_height+'"><img src="'+$_post_photo+'"/></a>');
$_displayed++;
if( $_displayed == $_display_limit) {
break;
}
}
}
jQuery('#tumblr_images').html($_photos.join(''));
},
}
);
function loadTumblrModal(id) {
var $_post = jQuery('#'+id),
$_post_next = $_post.next('.tumblr_post').attr('id'),
$_post_prev = $_post.prev('.tumblr_post').attr('id'),
$_post_link = $_post.attr('href'),
$_modal = jQuery('#tumblrModal'),
$_post_photo_src = $_post.attr('data-src-big').replace('http://','https://'),
$_post_photo_height = parseInt($_post.attr('data-src-big-height')),
$_post_photo_width = parseInt($_post.attr('data-src-big-width')),
$_post_photo_ratio = $_post_photo_width / $_post_photo_height,
$_window_height = jQuery(window).height(),
$_dialog_padding = 20,
$_dialog_margin = 20,
$_dialog_height = jQuery(window).height() - ($_dialog_margin * 2),
$_follow_iframe = '<iframe class="follow-button " src="https://platform.tumblr.com/v1/follow_button.html?button_type=1&tumblelog=theiloveuglyblog&color_scheme=dark" frameborder="0" scrolling="no" width="118" height="25"></iframe>';
// Re-Calibrate Dialog if Image is smaller than Window
if($_post_photo_height < $_dialog_height) {
$_dialog_height = $_post_photo_height;
}
// Set Dialog & Image Dimensions
var $_image_height = $_dialog_height - ($_dialog_padding *2),
$_image_width = $_image_height * $_post_photo_ratio,
$_dialog_width = $_image_width + ($_dialog_padding * 2);
// Force First/Last if Undefined for Next/Prev. Creates looping
if(typeof $_post_next === 'undefined') {
$_post_next = $_post.parent().find('.tumblr_post').first().attr('id');
}
if(typeof $_post_prev === 'undefined') {
$_post_prev = $_post.parent().find('.tumblr_post').last().attr('id');
}
// FireUp all the Modal
$_modal.find('.modal-dialog').css({
"width" : $_dialog_width + 'px',
"height" : $_dialog_height + 'px',
"margin-left" : -($_dialog_width/2) + 'px',
"margin-top" : -($_dialog_height/2) + 'px',
});
$_modal.find('.tumblr_img').html('<img src="'+$_post_photo_src+'" width="'+$_image_width+'" height="'+$_image_height+'"/>');
$_modal.find('.tumblr_iframe').html($_follow_iframe);
$_modal.find('#tumblr-next').attr('data-id',$_post_next);
$_modal.find('#tumblr-prev').attr('data-id',$_post_prev);
}
jQuery('#tumblr_images').on('click','a',function(e){
var $_modal = jQuery('#tumblrModal');
e.preventDefault();
loadTumblrModal(jQuery(this).attr('id'));
$_modal.modal('show');
});
jQuery(document).on('click','.tumblrLink',function(e){
loadTumblrModal(jQuery(this).attr('data-id'));
});
</script>