🖼 An image viewer for jetpack compose multiplatform.
一款基于Jetpack Compose Multiplatform开发的图片浏览库,支持过渡变换和超大图片的显示
The latest version:

🥳 1.1.1 全新版本~Scale支持Multiplatform啦!
📓 开发文档 👉 DOCS
🌟 案例
📷 RawCamera 👉 GITHUB
🌆 ImagePicker 👉 GITHUB
👌 特性
- 基于Jetpack Compose Multiplatform开发;
- 符合直觉的手势动效;
- 支持超大图片显示;
- 提供图片列表浏览组件;
- 支持图片弹出预览组件;
- 支持图片弹出预览的过渡动画;
- 支持定制化可扩展性高;
- 不依赖第三方图片库;
🖇️ 跨平台
| scale-zoomable-view | ✅ | ✅ | ✅ | ⛔️ |
| scale-image-viewer | ✅ | ✅ | ✅ | ⛔️ |
| scale-sampling-decoder | ✅ | ✅ | ✅ | ⛔️ |
| scale-image-viewer-classic | ⚠️废弃 | ⚠️废弃 | ⚠️废弃 | ⛔️ |
🧐 预览

📓 API
💽 接口文档 👉 API REFERENCE
👓 示例
👋 示例代码请参考:
Android 👉 SAMPLE-ANDROID
IOS 👉 SAMPLE-IOS
🛒 引入
Scale is available on mavenCentral()
repositories {
mavenCentral()
}
val version = "1.1.1-beta.3"
implementation("com.jvziyaoyao.scale:image-viewer:$version")
implementation("com.jvziyaoyao.scale:sampling-decoder:$version")
🛵 使用方式
1️⃣ 缩放组件
val painter = painterResource(id = R.drawable.light_02)
val state = rememberZoomableState(contentSize = painter.intrinsicSize)
ZoomableView(state = state) {
Image(
modifier = Modifier.fillMaxSize(),
painter = painter,
contentDescription = null,
)
}
2️⃣ 查看图片

val scope = rememberCoroutineScope()
val state = rememberZoomableState()
ImageViewer(
state = state,
model = painterResource(id = R.drawable.light_02),
modifier = Modifier.fillMaxSize(),
detectGesture = ZoomableGestureScope(onDoubleTap = {
scope.launch {
state.toggleScale(it)
}
})
)
3️⃣ 加载超大图

添加SamplingDecoder依赖支持:
implementation("com.jvziyaoyao.scale:sampling-decoder:$version")
‼ 仅在model类型为SamplingDecoder才会被当做大图进行加载
val bytes = remember { mutableStateOf<ByteArray?>(null) }
LaunchedEffect(Unit) { bytes.value = Res.readBytes("files/a350.jpg") }
val (samplingDecoder) = rememberSamplingDecoder(bytes.value)
if (samplingDecoder != null) {
val state = rememberZoomableState(
contentSize = samplingDecoder.intrinsicSize
)
ImageViewer(
model = samplingDecoder,
state = state,
processor = ModelProcessor(samplingProcessorPair),
)
}
4️⃣ 图片列表浏览

val images = remember {
mutableStateListOf(
R.drawable.light_01,
R.drawable.light_02,
)
}
ImagePager(
modifier = Modifier.fillMaxSize(),
pagerState = rememberZoomablePagerState { images.size },
imageLoader = { index ->
val painter = painterResource(images[index])
return@ImagePager Pair(painter, painter.intrinsicSize)
},
)
5️⃣ 图片弹出预览

val images = remember {
listOf(
R.drawable.img_01,
R.drawable.img_02,
)
}
val previewerState = rememberPreviewerState(pageCount = { images.size })
val scope = rememberCoroutineScope()
ImagePreviewer(
state = previewerState,
detectGesture = PagerGestureScope(onTap = {
scope.launch {
previewerState.close()
}
}),
imageLoader = { index ->
val painter = painterResource(id = images[index])
Pair(painter, painter.intrinsicSize)
}
)
previewerState.open()
6️⃣ 图片弹出预览(带转换效果)

val images = remember {
listOf(
Triple("001", R.drawable.thumb_01, R.drawable.img_01),
Triple("002", R.drawable.thumb_02, R.drawable.img_02),
)
}
val previewerState = rememberPreviewerState(
pageCount = { images.size },
getKey = { images[it].first }
)
val index = 1
val scope = rememberCoroutineScope()
TransformImageView(
modifier = Modifier
.size(120.dp)
.clickable {
scope.launch {
previewerState.enterTransform(index)
}
},
imageLoader = {
val key = images[index].first
val imageDrawableId = images[index].second
val painter = painterResource(id = imageDrawableId)
Triple(key, painter, painter.intrinsicSize)
},
transformState = previewerState,
)
ImagePreviewer(
state = previewerState,
detectGesture = PagerGestureScope(onTap = {
scope.launch {
previewerState.exitTransform()
}
}),
imageLoader = {
val painter = painterResource(id = images[it].third)
return@ImagePreviewer Pair(painter, painter.intrinsicSize)
}
)
🕵️♀️ 开源许可
Copyright 2022 jvziyaoyao
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.