When I use AnimatedVisibility around Canvas, it doesn't work.
AnimatedVisibility(
visible = firstShowVisible,
modifier = Modifier.align(Alignment.Center),
enter = fadeIn(0f, tween(300, 3100, LinearEasing))
) {
Canvas(modifier = Modifier) {
drawCircleBackground(color, radius, strokeWidth)
drawCircleProgress(color, progress, radius, strokeWidth)
}
}
The Canvas item would show immediately, rather than fade in slowly.
And firstShowVisible is changed by
var firstShowVisible by remember { mutableStateOf(false) }
LaunchedEffect(true) {
firstShowVisible = true
}
It works for other items, cannot work for Canvas only to me
It's solved.
It's because the arc I draw is not in the area of the outer Box.
When I make the outer Box fillMaxSize, it works.
Related
In this gesture multi-point control demo, it should be zooming and rotating with two fingers, but I found that one finger can be dragged in the test.
I want to drag with two fingers, how can I modify it?
#Composable
fun TransformableSample() {
// set up all transformation states
var scale by remember { mutableStateOf(1f) }
var rotation by remember { mutableStateOf(0f) }
var offset by remember { mutableStateOf(Offset.Zero) }
val state = rememberTransformableState { zoomChange, offsetChange, rotationChange ->
scale *= zoomChange
rotation += rotationChange
offset += offsetChange
}
Box(
Modifier
// apply other transformations like rotation and zoom
// on the pizza slice emoji
.graphicsLayer(
scaleX = scale,
scaleY = scale,
rotationZ = rotation,
translationX = offset.x,
translationY = offset.y
)
// add transformable to listen to multitouch transformation events
// after offset
.transformable(state = state)
.background(Color.Blue)
.fillMaxSize()
)
}
Modifier.transformable doesn't have much customization options, you can copy source code file to your project and replace
val panChange = event.calculatePan()
With
val panChange = if (event.changes.count() > 1) event.calculatePan() else Offset.Zero
You can create a Modifier as
Modifier
.pointerInput(Unit) {
awaitEachGesture {
// Wait for at least one pointer to press down
awaitFirstDown()
do {
val event = awaitPointerEvent()
// You can set this as required
if(event.changes.size==2){
offset = event.calculatePan()
// zoom
event.calculateZoom()
// rotation
event.calculateRotation()
}
// This is for preventing other gestures consuming events
// prevent scrolling or other continuous gestures
event.changes.forEach { pointerInputChange: PointerInputChange ->
pointerInputChange.consume()
}
} while (event.changes.any { it.pressed })
}
}
I also wrote a library that returns transform values with start, move, and end callbacks and number of pointer down that you can also use
https://github.com/SmartToolFactory/Compose-Extended-Gestures
Modifier.pointerInput(Unit) {
detectTransformGestures(
onGestureStart = {
transformDetailText = "GESTURE START"
},
onGesture = { gestureCentroid: Offset,
gesturePan: Offset,
gestureZoom: Float,
gestureRotate: Float,
mainPointerInputChange: PointerInputChange,
pointerList: List<PointerInputChange> ->
},
onGestureEnd = {
borderColor = Color.LightGray
transformDetailText = "GESTURE END"
}
)
}
Or this one that checks number of pointers and prequsite condition before returning zoom, rotate, drag and so on
Modifier
.pointerInput(Unit) {
detectPointerTransformGestures(
numberOfPointers = 1,
requisite = PointerRequisite.GreaterThan,
onGestureStart = {
transformDetailText = "GESTURE START"
},
onGesture = { gestureCentroid: Offset,
gesturePan: Offset,
gestureZoom: Float,
gestureRotate: Float,
numberOfPointers: Int ->
},
onGestureEnd = {
transformDetailText = "GESTURE END"
}
)
}
I have points generated one by one, and when a new point is generated, I want to draw a line segment connecting with the previous point. Like this:
var x by remember { mutableStateOf( 0.0f)}
var y by remember { mutableStateOf( 0.5f)}
var pStart by remember { mutableStateOf(Offset(0f, 0.5f))}
Canvas(modifier = Modifier.fillMaxSize()) {
canvasWidth = size.width
canvasHeight = size.height
val pEnd = Offset(x * canvasWidth, (1-y) * canvasHeight)
val col = if (pEnd.y < pStart.y) Color.Green else Color.Red
drawLine(
start = pStart,
end = pEnd,
strokeWidth = 4f,
color = col
)
pStart = pEnd
}
But this only draws the segment in a flash and no segments stay on the screen.
I know I can save the points to a list and redraw all the segments whenever a new point is added. But I just hope to economize. Is it possible?
There's no practical other way. You COULD in fact, keep track of just two points, adding a whole new Canvas (all Transparent and Filling the maximum Size, stacked on top of one another), for each extra point that is added. This does seem a bit impractical, but maybe try it out and do some benchmarking to see which one checks out. This is the only OTHER way I could think of, where you do not have to store all the points and recompose every time a point is added, since all the other lines would technically be frozen in space.
In response to the somewhat (unreasonably) aggressive comment below, here's some sample code. I assume you have a stream of new points coming in so a LiveData object is assumed to be the source of that, which I shall be converting to a MutableState<T> for my use-case.
var latestPoint by liveData.collectAsState()
var recordedPoint by remember { mutableStateOf(latestPoint) }
var triggerDraw by remember { mutableStateOf(false) }
var canvasList = mutableStateListOf<#Composable () -> Unit>Canvas>() // Canvas is the Composable
if(triggerDraw){
canvasList.add(
Canvas(){
/* you have the recordedPoint, and latestPoint, simply draw a line here */
}
)
triggerDraw = false
}
LaunchedEffect(latestPoint){
triggerDraw = true
}
canvasList.forEach {
it() // Invoke the Composable here
}
Thanks Dad!
I'm developing a basic window frame configurator. I splitted glasses in function below. I want to change color when i clicked and get the position of clicked glass to draw openin direction lines.
I tried to understand and implement Lavrton's method(https://codesandbox.io/s/0xj7zml2zl?file=/src/index.js) but i couldn't succeed.
function glassDraw(piece, frameWidth, frameHeight) {
var glassGroup = new Konva.Group();
for (i = 0; i <piece; i++) {
var glass = new Konva.Rect({
name: 'Rect',
x: padding + (frameWidth / piece) * i,
y: padding,
width: frameWidth / piece,
height: frameHeight - padding * 2,
fill: 'lightblue',
id: 'glass'+i,
});
glassGroup.add(glass);
}
glassGroup.find("Rect").on('click', function (e) {
// get id of the cube i drag
var clickedId = e.target.attrs.id;
$('#theId').html(clickedId);
})
return glassGroup;
}
When i use getelementbyid method with id of konva element("glass"+i), it returns null. I think i need to get as a konva element.
You have to create a click listener for all of your rectangles.
for (let rect of glassGroup.children) {
rect.on('click', () => {
console.log(rect.x(), rect.y()); // current position of the object
console.log(rect.id()); // log id of the object
rect.fill('green'); // set color to green
layer.batchDraw(); // update layer (batchDraw just for better performance .draw() would work to)
})
}
Make sure you always update the stage by either call stage.draw() or layer.draw() (or batchDraw() for better performance) after changing something, otherwise your stage will not show anything of what you do.
If something with this code don't work feel free to ask.
Have a nice day.
Is there an easy way to visualize a custom hit area shape?
As described here
https://konvajs.github.io/docs/events/Custom_Hit_Region.html
the hitFunc attribute can be set to a function that uses the supplied context to draw a custom hit area / region. Something like this:
var star = new Konva.Star({
...
hitFunc: function (context) {
context.beginPath()
context.arc(0, 0, this.getOuterRadius() + 10, 0, Math.PI * 2, true)
context.closePath()
context.fillStrokeShape(this)
}
})
For debugging purposes, I would like an easy way to toggle visual rendering of the shape (circle in this case), eg by filling it yellow.
Thanks :)
Currently, there is no public API for that. But you still can add hit canvas into the page somewhere and see how it looks:
const hitCanvas = layer.hitCanvas._canvas;
document.body.appendChild(hitCanvas);
// disable absolute position:
hitCanvas.style.position = '';
http://jsbin.com/mofocagupi/1/edit?js,output
Or you can add hit canvas on top of the stage and apply an opacity to make scene canvases visible:
const hitCanvas = layer.hitCanvas._canvas;
stage.getContainer().appendChild(hitCanvas);
hitCanvas.style.opacity = 0.5;
hitCanvas.style.pointerEvents = 'none';
http://jsbin.com/gelayolila/1/edit?js,output
is there a way to draw on the screen using my finger
i'm creating a Blackberry-10 application like draw something as a school assignment
If you have Qt Quick 2.0 available, you can make use the Canvas object in QML. The following sample shows how to draw a red line whenever there is a onPressed / onPositionChanged event:
import QtQuick 2.0
Rectangle {
property int startX;
property int startY;
property int finishX;
property int finishY;
Canvas {
anchors.fill: parent
onPaint: {
var ctx = getContext("2d");
ctx.fillStyle = "black";
ctx.fillRect(0, 0, width, height);
ctx.strokeStyle = "red";
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.lineTo(finishX, finishY);
ctx.stroke();
ctx.closePath();
}
MouseArea {
anchors.fill: parent
onPressed: {
startX = mouseX;
startY = mouseY;
}
onPositionChanged: {
finishX = mouseX;
finishY = mouseY;
parent.requestPaint();
}
}
}
}
Sorry but you are planning to use what a BB10 because I didn't understood the question.
Try this solution from the SDK documentation.