GraphicsLayer 重绘建议

本文探讨了如何通过将picmarksymbol嵌入资源来减少网络传输的方法,并介绍了设置autoMoveToTop为false以调整鼠标移动时的图形显示方式。
  1. 将picmarksymbol嵌入资源,因为可以减少网络传输
  2. autoMoveToTop 设置为false,这样鼠标移动上去是将不会显示到顶上而重绘graphic                                                                                         
@Composable private fun ContentView( uiState: JAFRSHO14010UiState, onAction: (action: JafAction) -> Unit, modifier: Modifier = Modifier, ) { // 描画ツールの状態 var isEraseMode by remember { mutableStateOf(false) } var isScrollMode by remember { mutableStateOf(false) } var selectedColor by remember { mutableStateOf(JafFF4C33) } var brushWidth by remember { mutableFloatStateOf(5f) } val eraseBrushWithMultiple = 2f val imageFoldPath = "storage/emulated/0/Download/Layout_image_01.png" val paths = remember { mutableStateListOf<Pair<Path, Paint>>() } var currentPath by remember { mutableStateOf<Path?>(null) } var redrawTrigger by remember { mutableIntStateOf(0) } var offsetX by remember { mutableFloatStateOf(0f) } var offsetY by remember { mutableFloatStateOf(0f) } // // LaunchedEffect(uiState.tegakiDrawData, currentImagePath){ // } paths.apply { addAll(loadPathsFromByteArray(uiState.tegakiDrawData ?: ByteArray(0))) } Column( modifier = Modifier .fillMaxSize() .padding(10.dp) ) { // メインコンテンツ Card( elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) ) { Row { // 描画ツールバー Column(Modifier.zIndex(2f)) { DrawingToolbar( modifier, uiState, onPrevImage = { onAction(JAFRSHO14010Action.OnBack) }, onNextImage = { onAction(JAFRSHO14010Action.OnForward) }, onEraseModeToggle = { isEraseMode = it onAction(JAFRSHO14010Action.OnReset) }, onImageScrollModelToggle = { isScrollMode = it onAction(JAFRSHO14010Action.OnScrollImage) }, onColorChange = { selectedColor = it onAction(JAFRSHO14010Action.OnSelectColor(it)) }, onBrushWidthChange = { brushWidth = it onAction(JAFRSHO14010Action.OnAdjustSize(it)) }, onZoomIn = { onAction(JAFRSHO14010Action.OnEnlargeImage) }, onZoomOut = { onAction(JAFRSHO14010Action.OnReduceImage) }, ) } Spacer(modifier = Modifier.width(10.dp)) Column( Modifier .size(893.dp, 507.dp) .zIndex(1f) ) { // 写真と描画キャンバス Box( modifier = Modifier .weight(1f) .clipToBounds() ) { // 背景写真 Image( bitmap = ImageBitmap(893, 507) .apply { val canvas = Canvas(this) val paint = Paint().apply { shader = LinearGradientShader( from = Offset(0f, 0f), to = Offset(893f, 507f), colors = listOf(Color.Cyan, Color.Magenta) ) } canvas.drawRect(Rect(0f, 0f, 893f, 507f), paint) }, contentDescription = null, modifier = Modifier .size(893.dp, 507.dp) .graphicsLayer( scaleX = uiState.imgScale, scaleY = uiState.imgScale, translationX = offsetX, translationY = offsetY ), contentScale = ContentScale.Fit ) Canvas( modifier = Modifier .size(893.dp, 507.dp) .graphicsLayer( scaleX = uiState.imgScale, scaleY = uiState.imgScale, compositingStrategy = CompositingStrategy.Offscreen ) .pointerInput(Unit) { awaitPointerEventScope { while (true) { val event = awaitPointerEvent() if (!isScrollMode) { val position = event.changes.first().position val pressed = event.changes.first().pressed if (pressed) { if (currentPath == null) { currentPath = Path().apply { moveTo( position.x - offsetX, position.y - offsetY ) } } else { currentPath?.lineTo( position.x - offsetX, position.y - offsetY ) } redrawTrigger++ } else if (currentPath != null) { currentPath?.let { val newPaint = Paint().apply { color = if (isEraseMode) { Color.Transparent } else { selectedColor } style = PaintingStyle.Stroke strokeWidth = if (isEraseMode) { brushWidth * eraseBrushWithMultiple } else { brushWidth } isAntiAlias = true strokeCap = StrokeCap.Round strokeJoin = StrokeJoin.Round } paths.add(it to newPaint) } currentPath = null redrawTrigger++ } event.changes.forEach { it.consume() } } else { val change = event.changes.first() if (event.changes.size == 1 && change.pressed) { val drag = change.positionChange() offsetX += drag.x offsetY += drag.y change.consume() } else { event.changes.forEach { it.consume() } } } } } } ) { redrawTrigger withTransform({ offsetX = offsetX.coerceIn( minimumValue = if (uiState.imgScale <= 1f) { 0f } else { -893f * uiState.imgScale.absoluteValue / 2 }, maximumValue = if (uiState.imgScale <= 1f) { 0f } else { 893f * uiState.imgScale.absoluteValue / 2 } // when (uiState.imgScale) { // 0f -> 0f // 1f -> 0f // 2f -> 893f / 2 // 3f -> 893f * 2 / 3 // 4f -> 893f * 3 / 4 // else -> 0f // } ) offsetY = offsetY.coerceIn( minimumValue = if (uiState.imgScale <= 1f) { 0f } else { -507f * uiState.imgScale.absoluteValue / 2 }, maximumValue = if (uiState.imgScale <= 1f) { 0f } else { 507f * uiState.imgScale.absoluteValue / 2 } // when (uiState.imgScale) { // 0f -> 0f // 1f -> 0f // 2f -> 507f / 2 // 3f -> 507f * 2 / 3 // 4f -> 507f * 3 / 4 // else -> 0f // } ) translate(left = offsetX, top = offsetY) }) { paths.forEach { (path, paint) -> if (paint.color == Color.Transparent) { drawPath( path = path, color = paint.color, style = Stroke(width = paint.strokeWidth), blendMode = BlendMode.Clear ) } else { drawPath( path = path, color = paint.color, style = Stroke(width = paint.strokeWidth) ) } } currentPath?.let { if (isEraseMode) { drawPath( path = it, color = selectedColor, style = Stroke( width = brushWidth * eraseBrushWithMultiple ), blendMode = BlendMode.Clear ) } else { drawPath( path = it, color = selectedColor, style = Stroke(width = brushWidth) ) } } } } } } } } Spacer(modifier = Modifier.height(10.dp)) Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(92.dp) ) { PageButton( text = uiState.btnReturn, { onAction(JAFRSHO14010Action.OnReturn) }, modifier.size(width = 220.dp, height = 75.dp) ) PageButton( text = uiState.btnClear, { if (!paths.isEmpty()) { paths.clear() currentPath = null redrawTrigger++ } onAction(JAFRSHO14010Action.OnClear) }, modifier.size(width = 220.dp, height = 75.dp) ) PageButton( text = uiState.btnUndo, { if (!paths.isEmpty()) { paths.removeAt(paths.lastIndex) redrawTrigger++ } onAction(JAFRSHO14010Action.OnUndo) }, modifier.size(width = 220.dp, height = 75.dp) ) PageButton( text = uiState.btnConfirm, { onAction(JAFRSHO14010Action.OnConfirm) }, modifier.size(width = 220.dp, height = 75.dp) ) } } // } }
最新发布
11-25
优化如下焰尾动效: import BaseLayerViewGL2D from "@arcgis/core/views/2d/layers/BaseLayerViewGL2D.js"; import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer.js"; import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js"; import { mat3, vec2, vec3, vec4 } from "gl-matrix"; const CustomLayerView2D = BaseLayerViewGL2D.createSubclass({ aPosition: 0, aOffset: 1, aDistance: 2, aSide: 3, aColor: 4, constructor: function () { this.transform = mat3.create(); this.extrude = mat3.create(); this.translationToCenter = vec2.create(); this.screenTranslation = vec2.create(); this.display = mat3.fromValues(NaN, 0, 0, 0, NaN, 0, -1, 1, 1); this.screenScaling = vec3.fromValues(NaN, NaN, 1); this.needsUpdate = false; }, attach: function () { const gl = this.context; // index buffers. const requestUpdate = () => { this.needsUpdate = true; this.requestRender(); }; this.watcher = reactiveUtils.on(() => this.layer.graphics, "change", requestUpdate); const vertexSource = ` precision highp float; uniform mat3 u_transform; uniform mat3 u_extrude; uniform mat3 u_display; attribute vec2 a_position; attribute vec2 a_offset; attribute float a_distance; attribute float a_side; attribute vec4 a_color; varying float v_distance; varying float v_side; varying vec4 v_color; void main(void) { gl_Position.xy = (u_display * (u_transform * vec3(a_position, 1.0) + u_extrude * vec3(a_offset, 0.0))).xy; gl_Position.zw = vec2(0.0, 1.0); v_distance = a_distance; v_side = a_side; v_color = a_color; }`; const fragmentSource = ` precision highp float; uniform float u_current_time; varying float v_distance; varying float v_side; varying vec4 v_color; const float TRAIL_SPEED = 50.0; const float TRAIL_LENGTH = 300.0; const float TRAIL_CYCLE = 1000.0; void main(void) { float d = mod(v_distance - u_current_time * TRAIL_SPEED, TRAIL_CYCLE); float a1 = d < TRAIL_LENGTH ? mix(0.0, 1.0, d / TRAIL_LENGTH) : 0.0; float a2 = exp(-abs(v_side) * 3.0); float a = a1 * a2; gl_FragColor = v_color * a; }`; const vertexShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertexShader, vertexSource); gl.compileShader(vertexShader); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragmentShader, fragmentSource); gl.compileShader(fragmentShader); // Create the shader program. this.program = gl.createProgram(); gl.attachShader(this.program, vertexShader); gl.attachShader(this.program, fragmentShader); // Bind attributes. gl.bindAttribLocation(this.program, this.aPosition, "a_position"); gl.bindAttribLocation(this.program, this.aOffset, "a_offset"); gl.bindAttribLocation(this.program, this.aDistance, "a_distance"); gl.bindAttribLocation(this.program, this.aSide, "a_side"); gl.bindAttribLocation(this.program, this.aColor, "a_color"); // Link. gl.linkProgram(this.program); // Shader objects are not needed anymore. gl.deleteShader(vertexShader); gl.deleteShader(fragmentShader); // Retrieve uniform locations once and for all. this.uTransform = gl.getUniformLocation( this.program, "u_transform" ); this.uExtrude = gl.getUniformLocation( this.program, "u_extrude" ); this.uDisplay = gl.getUniformLocation(this.program, "u_display"); this.uCurrentTime = gl.getUniformLocation( this.program, "u_current_time" ); this.vertexBuffer = gl.createBuffer(); this.indexBuffer = gl.createBuffer(); this.indexBufferSize = 0; if (gl.getParameter(gl.VERSION).startsWith("WebGL 2.0")) { this.vao = gl.createVertexArray(); this.bindVertexArray = (vao) => gl.bindVertexArray(vao); this.deleteVertexArray = (vao) => gl.deleteVertexArray(vao); } else { const vaoExt = gl.getExtension('OES_vertex_array_object'); this.vao = vaoExt.createVertexArrayOES(); this.bindVertexArray = (vao) => vaoExt.bindVertexArrayOES(vao); this.deleteVertexArray = (vao) => vaoExt.deleteVertexArrayOES(vao); } // Set up vertex attributes this.bindVertexArray(this.vao); gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.enableVertexAttribArray(this.aPosition); gl.enableVertexAttribArray(this.aOffset); gl.enableVertexAttribArray(this.aDistance); gl.enableVertexAttribArray(this.aSide); gl.enableVertexAttribArray(this.aColor); gl.vertexAttribPointer(this.aPosition, 2, gl.FLOAT, false, 28, 0); gl.vertexAttribPointer(this.aOffset, 2, gl.FLOAT, false, 28, 8); gl.vertexAttribPointer(this.aDistance, 1, gl.FLOAT, false, 28, 16); gl.vertexAttribPointer(this.aSide, 1, gl.FLOAT, false, 28, 20); gl.vertexAttribPointer(this.aColor, 4, gl.UNSIGNED_BYTE, true, 28, 24); this.bindVertexArray(null); this.centerAtLastUpdate = vec2.fromValues( this.view.state.center[0], this.view.state.center[1] ); }, detach: function () { this.watcher.remove(); const gl = this.context; gl.deleteBuffer(this.vertexBuffer); gl.deleteBuffer(this.indexBuffer); this.deleteVertexArray(this.vao); gl.deleteProgram(this.program); }, render: function (renderParameters) { const gl = renderParameters.context; const state = renderParameters.state; this.updatePositions(renderParameters); if (this.indexBufferSize === 0) { return; } mat3.identity(this.transform); this.screenTranslation[0] = (state.pixelRatio * state.size[0]) / 2; this.screenTranslation[1] = (state.pixelRatio * state.size[1]) / 2; mat3.translate( this.transform, this.transform, this.screenTranslation ); mat3.rotate( this.transform, this.transform, (Math.PI * state.rotation) / 180 ); this.screenScaling[0] = state.pixelRatio / state.resolution; this.screenScaling[1] = -state.pixelRatio / state.resolution; mat3.scale(this.transform, this.transform, this.screenScaling); mat3.translate( this.transform, this.transform, this.translationToCenter ); mat3.identity(this.extrude); mat3.rotate( this.extrude, this.extrude, (Math.PI * state.rotation) / 180 ); const HALF_WIDTH = 12; mat3.scale(this.extrude, this.extrude, [HALF_WIDTH, -HALF_WIDTH, 1]); // Update view `display` matrix; it converts from pixels to normalized device coordinates. this.display[0] = 2 / (state.pixelRatio * state.size[0]); this.display[4] = -2 / (state.pixelRatio * state.size[1]); // Draw. gl.useProgram(this.program); gl.uniformMatrix3fv(this.uTransform, false, this.transform); gl.uniformMatrix3fv(this.uExtrude, false, this.extrude); gl.uniformMatrix3fv(this.uDisplay, false, this.display); gl.uniform1f(this.uCurrentTime, performance.now() / 1000.0); this.bindVertexArray(this.vao); gl.enable(gl.BLEND); gl.blendFunc(gl.ONE, gl.ONE_MINUS_SRC_ALPHA); gl.drawElements( gl.TRIANGLES, this.indexBufferSize, gl.UNSIGNED_SHORT, 0 ); this.requestRender(); }, // Called internally from render(). updatePositions: function (renderParameters) { const gl = renderParameters.context; const stationary = renderParameters.stationary; const state = renderParameters.state; if (!stationary) { vec2.sub( this.translationToCenter, this.centerAtLastUpdate, state.center ); this.requestRender(); return; } if ( !this.needsUpdate && this.translationToCenter[0] === 0 && this.translationToCenter[1] === 0 ) { return; } this.centerAtLastUpdate.set(state.center); this.translationToCenter[0] = 0; this.translationToCenter[1] = 0; this.needsUpdate = false; const graphics = this.layer.graphics; let vtxCount = 0; let idxCount = 0; for (let i = 0; i < graphics.items.length; ++i) { const graphic = graphics.items[i]; const path = graphic.geometry.paths[0]; vtxCount += path.length * 2; idxCount += (path.length - 1) * 6; } const vertexData = new ArrayBuffer(7 * vtxCount * 4); const floatData = new Float32Array(vertexData); const colorData = new Uint8Array(vertexData); const indexData = new Uint16Array(idxCount); let vtxCursor = 0; let idxCursor = 0; for (let i = 0; i < graphics.items.length; ++i) { const graphic = graphics.items[i]; const path = graphic.geometry.paths[0]; const color = graphic.attributes["color"]; let s = {}; for (let j = 0; j < path.length; j++) { const p = path[j]; if (s.current) { s.delta = [p[0] - s.current[0], p[1] - s.current[1]]; const deltaLength = Math.sqrt(s.delta[0] * s.delta[0] + s.delta[1] * s.delta[1]); s.direction = [s.delta[0] / deltaLength, s.delta[1] / deltaLength]; const normal = [-s.direction[1], s.direction[0]]; if (s.normal) { s.offset = [s.normal[0] + normal[0], s.normal[1] + normal[1]]; // We first normalize it. const offsetLength = Math.sqrt(s.offset[0] * s.offset[0] + s.offset[1] * s.offset[1]); s.offset[0] /= offsetLength; s.offset[1] /= offsetLength; const d = s.normal[0] * s.offset[0] + s.normal[1] * s.offset[1]; s.offset[0] /= d; s.offset[1] /= d; } else { s.offset = [normal[0], normal[1]]; } floatData[vtxCursor * 7 + 0] = s.current[0] - this.centerAtLastUpdate[0]; floatData[vtxCursor * 7 + 1] = s.current[1] - this.centerAtLastUpdate[1]; floatData[vtxCursor * 7 + 2] = s.offset[0]; floatData[vtxCursor * 7 + 3] = s.offset[1]; floatData[vtxCursor * 7 + 4] = s.distance; floatData[vtxCursor * 7 + 5] = + 1; colorData[4 * (vtxCursor * 7 + 6) + 0] = color[0]; colorData[4 * (vtxCursor * 7 + 6) + 1] = color[1]; colorData[4 * (vtxCursor * 7 + 6) + 2] = color[2]; colorData[4 * (vtxCursor * 7 + 6) + 3] = 255; floatData[vtxCursor * 7 + 7] = s.current[0] - this.centerAtLastUpdate[0]; floatData[vtxCursor * 7 + 8] = s.current[1] - this.centerAtLastUpdate[1]; floatData[vtxCursor * 7 + 9] = -s.offset[0]; floatData[vtxCursor * 7 + 10] = -s.offset[1]; floatData[vtxCursor * 7 + 11] = s.distance; floatData[vtxCursor * 7 + 12] = -1; colorData[4 * (vtxCursor * 7 + 13) + 0] = color[0]; colorData[4 * (vtxCursor * 7 + 13) + 1] = color[1]; colorData[4 * (vtxCursor * 7 + 13) + 2] = color[2]; colorData[4 * (vtxCursor * 7 + 13) + 3] = 255; vtxCursor += 2; if (j >= 2) { indexData[idxCursor + 0] = vtxCursor - 4; indexData[idxCursor + 1] = vtxCursor - 3; indexData[idxCursor + 2] = vtxCursor - 2; indexData[idxCursor + 3] = vtxCursor - 3; indexData[idxCursor + 4] = vtxCursor - 1; indexData[idxCursor + 5] = vtxCursor - 2; idxCursor += 6; } s.normal = normal; s.distance += deltaLength; } else { s.distance = 0; } s.current = p; } s.offset = [s.normal[0], s.normal[1]]; floatData[vtxCursor * 7 + 0] = s.current[0] - this.centerAtLastUpdate[0]; floatData[vtxCursor * 7 + 1] = s.current[1] - this.centerAtLastUpdate[1]; floatData[vtxCursor * 7 + 2] = s.offset[0]; floatData[vtxCursor * 7 + 3] = s.offset[1]; floatData[vtxCursor * 7 + 4] = s.distance; floatData[vtxCursor * 7 + 5] = +1; colorData[4 * (vtxCursor * 7 + 6) + 0] = color[0]; colorData[4 * (vtxCursor * 7 + 6) + 1] = color[1]; colorData[4 * (vtxCursor * 7 + 6) + 2] = color[2]; colorData[4 * (vtxCursor * 7 + 6) + 3] = 255; floatData[vtxCursor * 7 + 7] = s.current[0] - this.centerAtLastUpdate[0]; floatData[vtxCursor * 7 + 8] = s.current[1] - this.centerAtLastUpdate[1]; floatData[vtxCursor * 7 + 9] = -s.offset[0]; floatData[vtxCursor * 7 + 10] = -s.offset[1]; floatData[vtxCursor * 7 + 11] = s.distance; floatData[vtxCursor * 7 + 12] = -1; colorData[4 * (vtxCursor * 7 + 13) + 0] = color[0]; colorData[4 * (vtxCursor * 7 + 13) + 1] = color[1]; colorData[4 * (vtxCursor * 7 + 13) + 2] = color[2]; colorData[4 * (vtxCursor * 7 + 13) + 3] = 255; vtxCursor += 2; indexData[idxCursor + 0] = vtxCursor - 4; indexData[idxCursor + 1] = vtxCursor - 3; indexData[idxCursor + 2] = vtxCursor - 2; indexData[idxCursor + 3] = vtxCursor - 3; indexData[idxCursor + 4] = vtxCursor - 1; indexData[idxCursor + 5] = vtxCursor - 2; idxCursor += 6; s.current = null; } // Upload data to the GPU. gl.bindBuffer(gl.ARRAY_BUFFER, this.vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, vertexData, gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this.indexBuffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW); // Record number of indices. this.indexBufferSize = indexData.length; } }); const CustomLayer = GraphicsLayer.createSubclass({ createLayerView: function (view) { if (view.type === "2d") { return new CustomLayerView2D({ view: view, layer: this }); } } }); export default CustomLayer;
09-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值