React实现文本框输入文字内容动态给图片添加文字信息(多个)并生成新的图片

这篇具有很好参考价值的文章主要介绍了React实现文本框输入文字内容动态给图片添加文字信息(多个)并生成新的图片。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

收到这个需求的时候,我的内心是崩溃的,脑子里已经跑过一万匹草泥马,内心想这种事为啥不交给ps去做,哪怕是手机里图片编辑也可以做到吧,专业的事交给专业的工具去干不就好了,何必出这种XX需求。后来想想就释然了,反正拿钱干活,干啥不是干,只要给钱,再XX的需求我也给你写出来。废话不多说,请看下方示例

实现效果:
React实现文本框输入文字内容动态给图片添加文字信息(多个)并生成新的图片,前端问题及案例,react.js,javascript,前端,canvas
本文案例只负责下图所示组件,上图中的图片列表只是为了方便展示
React实现文本框输入文字内容动态给图片添加文字信息(多个)并生成新的图片,前端问题及案例,react.js,javascript,前端,canvas

注:本组件使用的是React + AntD,这里样式就不过多描写了

思路一

首先,先说一开始错误的思路一,给图片添加文字信息,我一开始想到的是用canvas。先创建画布,获取要写入文字的图片宽高->再创建一个新的图片->然后再画布中画出刚才创建的新的图片->输入框文字发生改变则,获取输入框中输入的文字相关信息->在画布中绘制文字->生成新的图片渲染到页面。
但是最后该思路以失败告终,这条思路只适用于只添加一条文字信息的情况,添加第二条后就会出现添加的文字错乱的情况,因为新增代码后我无法保存上条图片的情况,无法知道上条图片的文字新增后的状态,如果是要删除情况又当如何,失败代码如下,如果只添加一条文字信息,可以用来参考,各位慢慢研究

import React from 'react'
import { Button } from 'antd'
import ParamsOptionComp from './ParamsOptionComp'

export default class MaterialEdit extends React.Component {
  canvas = document.createElement("canvas");
  image = React.createRef();

  state = {
    imgSrc: this.props.src,
    paramAttribute: [{
      content: '文字内容',
      color: '#000000',
      size: 24,
      left: 130,
      top: 50
    }],
    srcList: {
      0: this.props.src
    }
  }

  componentDidMount() {
    this.setState({
      paramAttribute: [{
        content: '文字内容',
        color: '#000000',
        size: 24,
        left: (this.image.current.clientWidth / 2) - 48,
        top: (this.image.current.clientHeight / 2) + 12
      }]
    }, () => {
      this.drawImage()
    })
    this.drawImage()
  }

  // 绘制图片
  drawImage = (params, index) => {
    // 创建画布
    const ctx = this.canvas.getContext("2d");
    // 获取图片大小
    const imageRect = this.image.current.getBoundingClientRect();

    const { width, height } = imageRect;
    this.canvas.width = width;
    this.canvas.height = height;

    // 创建图片
    const image = new Image();

    // if (index) {
    //   console.log(this.state.imgSrc, 123)
    //   image.src = this.state.imgSrc;
    // } else {
      image.src = this.props.src;
    // }

    image.onload = () => {
      ctx.drawImage(image, 0, 0, width, height);
      // 绘制文字
      this.drawText(params ? params : this.state.paramAttribute[0]);
    }

  }

  // 绘制文字
  drawText = ({ content, color, size, left, top }) => {
    // console.log(content, color, size, left, top)
    const ctx = this.canvas.getContext("2d");
    ctx.font = `${size}px Arial`;
    ctx.fillStyle = color;
    ctx.fillText(content, left, top);
    this.saveImage()
  };

  // 当文字发生变化
  onValuesChange = (changedValues, allValues) => {
    // console.log(changedValues, allValues);

    allValues.paramAttribute.forEach((item, index) => {
      // if (item && Object.keys(item).length > 0) {
        this.drawImage(item)
      // } else if (index >= 1) {
      //   this.drawImage(item, 'index')
      // }
    })
  }

  setSrcList = (index) => {
    console.log(index)
  }

  // 保存图片
  saveImage = () => {
    const image = this.canvas.toDataURL('image/png');
    // console.log(image);
    this.setState({ imgSrc: image });
    // 根据需要,将生成的图片显示给用户或保存为新的图片文件
    // 如:window.open(image);
  };

  // 将图片转成二进制格式
  base64ToBlob = (code) => {
    let parts = code.split(';base64,')
    let contentType = parts[0].split(':')[1]
    let raw = window.atob(parts[1])
    let rawLength = raw.length
    let uint8Array = new Uint8Array(rawLength)
    for (let i = 0; i < rawLength; i++) {
      uint8Array[i] = raw.charCodeAt(i)
    }
    return new Blob([uint8Array], { type: contentType })
  }

  // 下载图片
  download = (dataUrl, fileName) => {
    let aLink = document.createElement('a')
    let blob = this.base64ToBlob(dataUrl)
    let event = document.createEvent('HTMLEvents')
    event.initEvent('click', true, true)
    aLink.download = fileName + '.' + blob.type.split('/')[1]
    aLink.href = URL.createObjectURL(blob)
    aLink.click()
  }

  render() {
    const { imgSrc } = this.state
    const { src } = this.props
    return <>
      <div style={{ textAlign: 'center', marginBottom: '30px' }}>
        <img src={imgSrc} width={300} ref={this.image} />
      </div>
      <ParamsOptionComp onValuesChange={this.onValuesChange} paramAttribute={this.state.paramAttribute} setSrcList={this.setSrcList}/>
      <Button type="primary" htmlType="submit">
        生成新的图片
      </Button>
    </>
  }
}

思路二

同样也是与canvas相关,不过这次想到的是,将输入的文字信息生成节点添加到页面中的某个模块,然后再将这个模块输出成canvas然后再转化成图片下载下来,这里会用到html2canvas这个库,这个库可以让html页面和canvas相互转换,大致思路如下:

下载html2canvas依赖包->搭建页面,并创建新增节点的区域->初始化新增第一个节点到页面中的某个指定模块->当文本框发生变动,修改节点信息->实现节点删除->利用html2canvas将模块生成canvas,然后转化成图片

1. 下载html2canvas依赖包
npm i html2canvas
2. 搭建页面,并且创建新增节点的区域

MaterialEditComp.jsx

import React, { useEffect, useRef, useState } from 'react'
import { Button } from 'antd'
import ParamsOptionComp from './ParamsOptionComp'
import html2canvas from 'html2canvas';
import './index.less'

export default function MaterialEditComp(props) {
  const { src, getNewImage } = props;
  const [imgSrc, setImgSrc] = React.useState(src);
  const contentRef = useRef(null);
  const [imageSize, setImageSize] = useState({ width: 0, height: 0 });

  const onValuesChange = (changedValues, allValues) => {}

  // 新增节点
  const AddNode = ({content, color, size, left, top }, index) => {}

  //  删除节点
  const removeNode = (index) => {}
  
  // 将图片转换成二进制形式
  const base64ToBlob = (code) => {}

  // 保存图片
  const saveImage = async () => {}


  useEffect(() => {
  	// 坑一
    const img = new Image();
    img.src = imgSrc

    img.onload = () => {
      setImageSize({ width: img.width, height: img.height });
    };
  }, []);


  return (
    <>
      // 新增节点区域
        <div ref={contentRef} style={{
        background: `url(${imgSrc}) no-repeat`,
        backgroundSize: 'cover',
        backgroundPosition: 'center',
        width: imageSize.width + 'px',
        height: imageSize.height + 'px',
        marginBottom: '30px'
      }} className="content">
        
      </div>
      // 输入框组件
      <ParamsOptionComp onValuesChange={onValuesChange} imageSize={imageSize} removeNode={removeNode} />
      <Button type="primary" htmlType="submit" onClick={saveImage} className='btn'>
        生成新的图片
      </Button>
    </>
  )
}

ParamsOptionComp.jsx

import React, { useEffect, useState } from 'react'
import { Input, Form, Space, Button, InputNumber } from 'antd'
import { PlusOutlined, MinusCircleOutlined, DragOutlined } from '@ant-design/icons'

export default function ParamsOptionComp(props) {
  const { onValuesChange, imageSize, removeNode } = props
  const [count, setCount] = useState(0)
  const [form] = Form.useForm();

  // 坑一
  useEffect(() => {
    form.resetFields()
  }, [imageSize])


  return <Form form={form} name="dynamic_form_nest_item"
    // 坑一
    initialValues={{
      paramAttribute: [{
        left: imageSize.width / 2 - 48,
        top: imageSize.height / 2 - 24,
        content: '文字内容'
      }]
    }}
    onValuesChange={onValuesChange} >

    <Form.List name="paramAttribute">
      {(fields, { add, remove, move }) => (
        <>
          {fields.map(({ key, name, ...restField }, index) => (
            <Space key={key} style={{ display: 'flex', marginBottom: 0 }} align="baseline">
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'content']}
                label="文字内容"
                rules={[{ required: true, message: '请输入文字内容' }]}
              >
                <Input placeholder="文字内容" maxLength={50} />
              </Form.Item>
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'color']}
                label="文字颜色"
                rules={[{ required: true, message: '请输入文字颜色' }]}
                initialValue="#000000"
              >
                <Input placeholder="文字颜色" type="color" style={{ width: '80px' }} />
              </Form.Item>
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'size']}
                label="文字大小"
                rules={[{ required: true, message: '请输入文字大小' }]}
                initialValue="24"
              >
                <InputNumber placeholder="文字大小" min={12} value={13} />
              </Form.Item>
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'top']}
                label="上边距"
                rules={[{ required: true, message: '请输入上边距' }]}
                initialValue={imageSize.height / 2 - 24}
              >
                <InputNumber placeholder="上边距" value={13} />
              </Form.Item>
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'left']}
                label="左边距"
                rules={[{ required: true, message: '请输入左边距' }]}
                initialValue={imageSize.width / 2 - 48}
              >
                <InputNumber placeholder="左边距" value={13} />
              </Form.Item>
              <MinusCircleOutlined onClick={() => {
                if (count === 0) {
                  return
                }
                remove(name)
                removeNode(index)
                setCount(count => count - 1);
              }} />
            </Space>
          ))}
          <Form.Item>
            <Button type="dashed" onClick={async () => {
              try {
                const values = await form.validateFields()
                add();
                setCount(count => count + 1);
              } catch (errorInfo) {
                return;
              }
            }} block icon={<PlusOutlined />}>添加选项</Button>
          </Form.Item>
        </>
      )}
    </Form.List>
  </Form>
}

此时页面如下
React实现文本框输入文字内容动态给图片添加文字信息(多个)并生成新的图片,前端问题及案例,react.js,javascript,前端,canvas
上述代码中有个坑

  • 因为产品要求,需要给个初始化数据,并且文字要在图片内水平垂直居中但是初始化数据时是无法获取到图片的宽高的,并且初始化时在useEffect中也无法通过ref获取图片的大小,此时只能将接收到的图片生成新的图片,然后读取新图片的宽高赋值给ImageSize,此时方可获取到图片真正的宽高,然后再传递给ParamsOptionComp组件

    MaterialEditComp.jsx

    const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
    
    useEffect(() => {
        const img = new Image();
        img.src = imgSrc
    
        img.onload = () => {
          setImageSize({ width: img.width, height: img.height });
    	};
    }, []);
    <ParamsOptionComp onValuesChange={onValuesChange} imageSize={imageSize} removeNode=		 {removeNode} />
    

    ParamsOptionComp.jsx

    下方初始化第一条数据时,因为initialValue就是所谓的defaultValue,只会在第一次赋值的时候改变,无法直接设置initialValue的值来改变,所以获取到的也是第一次初始化的宽高都为0,但是我们 可以通过Form的resetFields()方法来解决这个问题,当监听到imageSize发生变化时我们可以调用resetFields()来重新设置initialValue的值。
    React实现文本框输入文字内容动态给图片添加文字信息(多个)并生成新的图片,前端问题及案例,react.js,javascript,前端,canvas

    useEffect(() => {
      form.resetFields()
    }, [imageSize])
    
3. 初始化新增第一个节点到页面中的某个指定模块

此时输入框中已经有值了,但是此处图片中还没有初始化值,此时,需要在useEffect中调用AddNode初始化第一个节点的值。

const AddNode = ({ content, color, size, left, top }, index) => {
    const contentNode = contentRef.current;
    let newNode = document.createElement('div');
    newNode.className = 'node' + index;
    // 此处判断节点是否已经存在
    const bool = contentNode?.childNodes[index]
    if (bool) {
      newNode = contentNode.childNodes[index]
    }

    newNode.textContent = content
    newNode.style.color = color;
    newNode.style.fontSize = size + 'px';
    newNode.style.top = top + 'px';
    newNode.style.left = left + 'px';
    newNode.style.position = 'absolute';

    // 节点不存在新增阶段
    if (!bool) {
      contentNode.appendChild(newNode);
    } else {
      // 节点存在则替换原来的节点
      contentNode.replaceChild(newNode, contentNode.childNodes[index])
    }
  }
  
   useEffect(() => {
    const img = new Image();
    img.src = imgSrc

    img.onload = () => {
      setImageSize({ width: img.width, height: img.height });
      AddNode({
        content: '文字内容',
        color: '#000000',
        size: 24,
        left: img.width / 2 - 48,
        top: img.height / 2 - 24
      }, 0);
    };
  }, []);
4. 当文本框发生变动,修改节点信息

当文本框发生变动通过表单的onValuesChange 进行监听,遍历表单中的数据新增节点信息

const onValuesChange = (changedValues, allValues) => {
    // index标记当前是第几个节点
    allValues.paramAttribute.forEach((item, index) => {
      item && AddNode(item, index)
    })
  }
5. 实现节点删除

当进行节点删除时,调用传递给ParamsOptionComp组件的removeNode方法获取到删除的节点

  //  删除节点
  const removeNode = (index) => {
    const contentNode = contentRef.current;
    const bool = contentNode?.childNodes[index]
    if (bool) {
      contentNode.removeChild(contentNode.childNodes[index])
    }
  }
6. 利用html2canvas将模块生成canvas,然后转化成图片

此时需要利用html2canvas将模块生成canvas,然后转化成图片,如果需要调用接口将图片保存下来,此处还需将图片转换成二进制,如果不需要则直接下载就好

  // 将图片转换成二进制形式
  const base64ToBlob = (code) => {
    let parts = code.split(';base64,')
    let contentType = parts[0].split(':')[1]
    let raw = window.atob(parts[1])
    let rawLength = raw.length
    let uint8Array = new Uint8Array(rawLength)
    for (let i = 0; i < rawLength; i++) {
      uint8Array[i] = raw.charCodeAt(i)
    }
    return new Blob([uint8Array], { type: contentType })
  }

  // 保存图片
  const saveImage = async () => {
    const contentNode = contentRef.current;
    const canvas = await html2canvas(contentNode, {
      useCORS: true,
      allowTaint: true,//允许污染
      backgroundColor: '#ffffff',
      // toDataURL: src
    })

    const imgData = canvas.toDataURL('image/png');
    let blob = base64ToBlob(imgData)
    const link = document.createElement('a');
    link.href = imgData;
    // link.href = URL.createObjectURL(blob);
    getNewImage(link.href)
    // console.log(blob, 11)
    link.download = 'page-image.' + blob.type.split('/')[1];
    link.click();
  }

注意:此处使用html2canvas时, 必须配置以下信息,否则图片信息无法进行转换

{
      useCORS: true,
      allowTaint: true,//允许污染
      backgroundColor: '#ffffff',
      // toDataURL: src
    }

完整代码

MaterialEditComp.jsx

import React, { useEffect, useRef, useState } from 'react'
import { Button } from 'antd'
import ParamsOptionComp from './ParamsOptionComp'
import html2canvas from 'html2canvas';
import './index.less'

export default function MaterialEditComp(props) {
  const { src, getNewImage } = props;
  const [imgSrc, setImgSrc] = React.useState(src);
  const contentRef = useRef(null);
  const [imageSize, setImageSize] = useState({ width: 0, height: 0 });

/* 
  const divStyle = {
    background: `url(${imgSrc}) no-repeat`,
    backgroundSize: 'cover',
    backgroundPosition: 'center',
    width: imageSize.width + 'px',
    height: imageSize.height + 'px',
    marginBottom: '30px'
  }; */

  const onValuesChange = (changedValues, allValues) => {
    // console.log(changedValues, allValues, 11)
    allValues.paramAttribute.forEach((item, index) => {
      item && AddNode(item, index)
    })
  }

  // 新增节点
  const AddNode = ({ content, color, size, left, top }, index) => {
    const contentNode = contentRef.current;
    let newNode = document.createElement('div');
    newNode.className = 'node' + index;
    // 此处判断节点是否已经存在
    const bool = contentNode?.childNodes[index]
    if (bool) {
      newNode = contentNode.childNodes[index]
    }

    newNode.textContent = content
    newNode.style.color = color;
    newNode.style.fontSize = size + 'px';
    newNode.style.top = top + 'px';
    newNode.style.left = left + 'px';
    newNode.style.position = 'absolute';

    // 节点不存在新增阶段
    if (!bool) {
      contentNode.appendChild(newNode);
    } else {
      // 节点存在则替换原来的节点
      contentNode.replaceChild(newNode, contentNode.childNodes[index])
    }
  }

  //  删除节点
  const removeNode = (index) => {
    const contentNode = contentRef.current;
    const bool = contentNode?.childNodes[index]
    if (bool) {
      contentNode.removeChild(contentNode.childNodes[index])
    }
  }
  // 将图片转换成二进制形式
  const base64ToBlob = (code) => {
    let parts = code.split(';base64,')
    let contentType = parts[0].split(':')[1]
    let raw = window.atob(parts[1])
    let rawLength = raw.length
    let uint8Array = new Uint8Array(rawLength)
    for (let i = 0; i < rawLength; i++) {
      uint8Array[i] = raw.charCodeAt(i)
    }
    return new Blob([uint8Array], { type: contentType })
  }

  // 保存图片
  const saveImage = async () => {
    const contentNode = contentRef.current;
    const canvas = await html2canvas(contentNode, {
      useCORS: true,
      allowTaint: true,//允许污染
      backgroundColor: '#ffffff',
      // toDataURL: src
    })

    const imgData = canvas.toDataURL('image/png');
    let blob = base64ToBlob(imgData)
    const link = document.createElement('a');
    link.href = imgData;
    // link.href = URL.createObjectURL(blob);
    getNewImage(link.href)
    // console.log(blob, 11)
    link.download = 'page-image.' + blob.type.split('/')[1];
    link.click();
  }


  useEffect(() => {
    const img = new Image();
    img.src = imgSrc

    img.onload = () => {
      setImageSize({ width: img.width, height: img.height });
      AddNode({
        content: '文字内容',
        color: '#000000',
        size: 24,
        left: img.width / 2 - 48,
        top: img.height / 2 - 24
      }, 0);
    };
  }, []);


  return (
    <>
      <div ref={contentRef} style={{
        background: `url(${imgSrc}) no-repeat`,
        backgroundSize: 'cover',
        backgroundPosition: 'center',
        width: imageSize.width + 'px',
        height: imageSize.height + 'px',
        marginBottom: '30px'
      }} className="content">

      </div>
      <ParamsOptionComp onValuesChange={onValuesChange} imageSize={imageSize} removeNode={removeNode} />
      <Button type="primary" htmlType="submit" onClick={saveImage} className='btn'>
        生成新的图片
      </Button>
    </>
  )
}

ParamsOptionComp.jsx

import React, { useEffect, useState } from 'react'
import { Input, Form, Space, Button, InputNumber } from 'antd'
import { PlusOutlined, MinusCircleOutlined, DragOutlined } from '@ant-design/icons'

export default function ParamsOptionComp(props) {
  const { onValuesChange, imageSize, removeNode } = props
  const [count, setCount] = useState(0)
  const [form] = Form.useForm();

  // 坑
  useEffect(() => {
    form.resetFields()
  }, [imageSize])


  return <Form form={form} name="dynamic_form_nest_item"
    // 坑
    initialValues={{
      paramAttribute: [{
        left: imageSize.width / 2 - 48,
        top: imageSize.height / 2 - 24,
        content: '文字内容'
      }]
    }}
    onValuesChange={onValuesChange} >

    <Form.List name="paramAttribute">
      {(fields, { add, remove, move }) => (
        <>
          {fields.map(({ key, name, ...restField }, index) => (
            <Space key={key} style={{ display: 'flex', marginBottom: 0 }} align="baseline">
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'content']}
                label="文字内容"
                rules={[{ required: true, message: '请输入文字内容' }]}
              >
                <Input placeholder="文字内容" maxLength={50} />
              </Form.Item>
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'color']}
                label="文字颜色"
                rules={[{ required: true, message: '请输入文字颜色' }]}
                initialValue="#000000"
              >
                <Input placeholder="文字颜色" type="color" style={{ width: '80px' }} />
              </Form.Item>
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'size']}
                label="文字大小"
                rules={[{ required: true, message: '请输入文字大小' }]}
                initialValue="24"
              >
                <InputNumber placeholder="文字大小" min={12} value={13} />
              </Form.Item>
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'top']}
                label="上边距"
                rules={[{ required: true, message: '请输入上边距' }]}
                initialValue={imageSize.height / 2 - 24}
              >
                <InputNumber placeholder="上边距" value={13} />
              </Form.Item>
              <Form.Item
                style={{ marginBottom: '10px' }}
                {...restField}
                name={[name, 'left']}
                label="左边距"
                rules={[{ required: true, message: '请输入左边距' }]}
                initialValue={imageSize.width / 2 - 48}
              >
                <InputNumber placeholder="左边距" value={13} />
              </Form.Item>
              <MinusCircleOutlined onClick={() => {
                if (count === 0) {
                  return
                }
                remove(name)
                removeNode(index)
                setCount(count => count - 1);
              }} />
            </Space>
          ))}
          <Form.Item>
            <Button type="dashed" onClick={async () => {
              try {
                const values = await form.validateFields()
                add();
                setCount(count => count + 1);
              } catch (errorInfo) {
                return;
              }
            }} block icon={<PlusOutlined />}>添加选项</Button>
          </Form.Item>
        </>
      )}
    </Form.List>
  </Form>
}

使用:文章来源地址https://www.toymoban.com/news/detail-739739.html

const src=''
const getNewImage = (image) => {
    console.log(image)
    page.close()
 }
return <MaterialEditComp src={src}  getNewImage={getNewImage} />

到了这里,关于React实现文本框输入文字内容动态给图片添加文字信息(多个)并生成新的图片的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 怎么在PDF添加文本框?6种快速向PDF添加文字教程

    有时您可能希望填写表格或在 PDF 文件中留下评论。这需要您将文本框和文本添加到 PDF。文本框是一个文本字段,您可以在其中键入文本。但是,除非您使用专用的 PDF 编辑器,否则编辑 PDF 文件具有挑战性。了解正确的 PDF 工具和将文本框添加到 PDF的简单方法可以帮助您高效

    2024年01月23日
    浏览(27)
  • tinymce4/5实现将word中内容(文字图片等)直接粘贴至编辑器中——利用插件tinymce-powerpaste-plugin

    TinyMCE是一款易用、且功能强大的所见即所得的富文本编辑器。同类程序有:UEditor、Kindeditor、Simditor、CKEditor、wangEditor、Suneditor、froala等等。 TinyMCE的优势: 开源可商用,基于LGPL2.1 插件丰富,自带插件基本涵盖日常所需功能(示例看下面的Demo-2) 接口丰富,可扩展性强,有

    2024年02月15日
    浏览(29)
  • React Native 文本输入基础知识

    在 React Native 中提供了一个文本输入组件 TextInput 。此组件主要是监听键盘输入事件,并把对应的输入值显示在组件中,此组件还提供了很多功能配置参数,例如自动更正、自动大写、占位符文本和不同的键盘类型(例如数字键盘)。 我们首先来编写一个简单的实例,使用

    2024年02月12日
    浏览(24)
  • React Native文本添加下划线

    2024年02月13日
    浏览(24)
  • Qt图片编辑 - 在直线添加文字

    在绘制一条直线时,比如说在直线中间输出文字,如下图所示 本质上不难,定位到位置,然后drawText就可以了 难就难在 文字要跟随线的斜率,例如    还有,文字最好保证在线的“上方” 首先是角度问题 这个角度跟线Line与X轴夹角是一致的,因此,只要有线两端坐标就可以

    2024年02月15日
    浏览(31)
  • Python代码学习之给图片添加文字或图片水印

    图片加水印有什么好处?在现今的数字化时代,网络上的图片泛滥,盗图现象也越来越严重。因此,在发布文章时,为了保护自己的原创作品版权,很多人选择使用水印来保护他们的图片。这样就能更好地做到: 1.版权保护:在商业用途的照片中添加水印可以帮助保护作者的

    2024年02月09日
    浏览(40)
  • textarea文本框根据输入内容自动适应高度

    第一种: 第二种: 加一个监听该文本框内容变化的方法  oninput ,然后在该方法里手动计算文本框的高度并实现自适应:

    2024年01月21日
    浏览(31)
  • React Native从文本内容尾部截取显示省略号

    参考链接: https://www.reactnative.cn/docs/text#ellipsizemode https://chat.xutongbao.top/

    2024年02月14日
    浏览(30)
  • jQuery 在图片和文字中插入内容(多种情况考虑)

    昨天接到一个新的需要,在后台文章编辑器中,每一个文章的正文前面,可以单独添加一个电头字段,但是如果在富文本编辑器中最上面就添加图片的话,图片就会把电头和正文中的文字给隔开。需要做的是获取到电头字段,然后在正文中的文字部分的最前面插入电头字段。

    2023年04月25日
    浏览(24)
  • Vue+element-ui的el-cascader实现动态添加删除级联地点输入框

    实现省市区三级地点级联选择,可联想; 包括始发地点、途径地点、终止地点,始发地点、终止地点均为一个,途径地点可以没有也可以是多个; 用户可以动态添加/删除途径地点。 使用级联选择器Cascader需要的树形数据,前端请求到后端获取省市区数据并处理为elementui官网

    2024年02月04日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包