简体   繁体   English

React Native 中图像顶部的叠加按钮

[英]Overlay button on top of Image in React Native

I'm trying to achieve the following effect in React Native:我正在尝试在 React Native 中实现以下效果:

角落带有图标的图像

The image has a button in the corner.图像的角落有一个按钮。 The button is always within the corner of the image regardless of the image's size or aspect ratio, and no part of the image is clipped (it is always scaled down to fit fully within a box).无论图像的大小或纵横比如何,该按钮始终位于图像的角落内,并且图像的任何部分都不会被剪裁(它总是按比例缩小以完全适合一个框)。

The trouble I'm having in React Native is that the Image component's size doesn't always match the scaled-down size of the image.我在 React Native 中遇到的问题是 Image 组件的大小并不总是与缩小的图像大小匹配。 If I fix the image's height to 300, set flex 1 to make the image's width expand to fill its contents, and the image is a portrait, the Image component with being the full width of the container, but the image within the component will have a width of much less.如果我将图像的高度固定为 300,设置 flex 1 使图像的宽度扩展以填充其内容,并且图像是肖像,图像组件具有容器的全宽,但组件内的图像将具有宽度要小得多。 Therefore, the typical approach for having a view overlay another view doesn't work as I would like it to- my overlay also covers the padding around the image, and the button (anchored to the corner) appears outside of the image.因此,让视图覆盖另一个视图的典型方法并不像我希望的那样工作 - 我的覆盖层还覆盖了图像周围的填充,并且按钮(锚定到角落)出现在图像之外。

Here's what it looks like in React Native:这是在 React Native 中的样子:

React Native 中带有按钮叠加的肖像图像

The X is a placeholder for the button. X 是按钮的占位符。 It is set to anchor to the top-left of a View that's a child of the same View that the Image is a child of.它被设置为锚定到视图的左上角,该视图是图像的子视图的子视图。 The backgroundColor of the image is set to green to demonstrate how the width of the Image component is different from the width of the picture that's inside the component.图片的 backgroundColor 设置为绿色,以演示 Image 组件的宽度与组件内部图片的宽度有何不同。

The goal is that the X would be inside of the image regardless of its aspect ratio.目标是无论纵横比如何,X 都将位于图像内部。 I think I could do something based on grabbing the image's dimension and scaling the height and width of the Image component, but that sounds complicated and fragile.我想我可以根据抓取图像的尺寸并缩放 Image 组件的高度和宽度来做一些事情,但这听起来既复杂又脆弱。 Is this possible in a responsive way with styling?这是否可以通过样式响应方式实现?

Demonstration code:演示代码:

   <View
      style={{
        marginLeft: 7,
        marginRight: 7,
        backgroundColor: 'blue',
      }}
    >
      <View
        style={{
          height: 300,
          flex: 1,
        }}
      >
        <Image
          source={imageSource}
          style={{
            flex: 1,
            height: undefined,
            width: undefined,
            backgroundColor: 'green',
          }}
          resizeMode="contain"
        />
      </View>
      <View
        style={{
          position: 'absolute',
          right: 5,
          top: 5,
          backgroundColor: 'transparent',
        }}
      >
        <Text style={{ color: 'white' }}>X</Text>
      </View>
    </View>

From React-native v0.50.0 <Image> with nested content is no longer supported.从 React-native v0.50.0 开始,不再支持带有嵌套内容的<Image> Use <ImageBackground> instead.请改用<ImageBackground>

<ImageBackground
  source={imageSource}
>
    <View>
        <Text>×</Text>
    </View>
</ImageBackground>

Here is how I accomplished that:这是我如何实现的:

import React, { Component } from "react";
import { View, Image, StyleSheet } from "react-native";
import { Ionicons } from "@expo/vector-icons";

class MyCard extends Component {
  render() {
    return (
      <View style={styles.container}>
        <Image
          resizeMode="cover"
          style={styles.cover}
          source={{ uri: "https://picsum.photos/700" }}
        />
        <Ionicons style={styles.close} name="ios-close-circle" size={25} />
      </View>
    );
  }
}

export default MyCard;

const styles = StyleSheet.create({
  container: {
    margin: 5,
    width: 160,
    height: 200
  },
  cover: {
    flex: 1,
    borderRadius: 5
  },
  close: {
    margin: 5,
    position: "absolute",
    top: 0,
    left: 0,
    width: 25,
    height: 25,
    color: "tomato"
  }
});

Here is how it looks like :这是它的样子:

在此处输入图片说明

Updated 29 Jun 2019 2019 年 6 月 29 日更新

Now( react-native@0.60-RC ) there's a specific component to wrap elements as image background, ImageBackground .现在( react-native@0.60-RC )有一个特定的组件可以将元素包装为图像背景ImageBackground Go for the official documentation.去官方文档。

Following Gist has been changed.以下要点已更改。


Original Answer原答案

We can use <Image/> display images, and we can use it as a background-image hack.我们可以使用<Image/>显示图像,我们可以将其用作background-image黑客。

try this试试这个

<Image
  source={imageSource}
>
  <View>
    <Text>×</Text>
  </View>
</Image>

this gist is a full demo for your need. 这个要点是满足您需求的完整演示。

or you can see it live at expo:或者你可以在世博会上看到它:

 <div data-snack-id="B1SsJ7m2b" data-snack-platform="ios" data-snack-preview="true" data-snack-theme="light" style="overflow:hidden;background:#fafafa;border:1px solid rgba(0,0,0,.16);border-radius:4px;height:505px;width:100%"></div> <script async src="https://snack.expo.io/embed.js"></script>

So the way you do this, as @ObooChin mentioned, is to use Image.getSize() to get the actual size of the image, then generate a height and width based on a) the maximum space you want the image to take up, and b) the aspect ratio of the actual image's dimensions.所以你这样做的方式,正如@ObooChin 提到的,是使用 Image.getSize() 来获取图像的实际大小,然后根据 a) 你希望图像占用的最大空间生成高度和宽度, b) 实际图像尺寸的纵横比。

import React, { Component } from 'react';
import {
  StyleSheet,
  Image,
  View,
  Dimensions,
} from 'react-native';

export default class FlexibleThumbnail extends Component {

    constructor(props) {
        super(props)

        this.state = {
            imageWidth: 0,
            imageHeight: 0,
            source: null,
        }
    }

    componentWillMount() {
        this._updateState(this.props)
    }

    componentWillReceiveProps(nextProps) {
        this._updateState(nextProps)
    }

    _updateState(props) {
        const {source} = props;
        const height = props.maxHeight;
        const width = props.maxWidth || Dimensions.get('window').width;

        const imageUri = source.uri;

        Image.getSize(imageUri, (iw, ih) => {
            const {imageWidth, imageHeight} = /* some function that takes max height, width and image height, width and outputs actual dimensions image should be */

            this.setState({
                imageWidth,
                imageHeight,
                source,
            });
        });
    }

    render() {
        const {source, height, width, imageWidth, imageHeight} = this.state;

        const overlay = (/* JSX for your overlay here */);

        // only display once the source is set in response to getSize() finishing
        if (source) {
            return (
                <View style={{width: imageWidth, height: imageHeight}} >
                    <Image
                        style={[ imageStyle, {width: imageWidth, height: imageHeight} ]}
                        resizeMode="contain"
                        source={source}
                    />
                        <View style={{
                            position: 'absolute',
                            top: 0,
                            bottom: 0,
                            left: 0,
                            right: 0,
                            backgroundColor: 'transparent',
                        }}>
                            {overlay}
                        </View>
                </View>
            );
        }

        return (
            <View />
        )
    }
}

The attempt is still a bit rough, but I'm working on turning this into a library where you can specify max height, width, and the overlay you want to use, and it handles the rest: https://github.com/nudgeyourself/react-native-flexible-thumbnail .尝试仍然有点粗糙,但我正在努力将其变成一个库,您可以在其中指定最大高度、宽度和要使用的叠加层,并处理其余部分: https : //github.com/轻推你自己/react-native-flexible-thumbnail

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM